Bump-allocate LoadedModule

This commit is contained in:
Richard Feldman 2020-07-30 18:40:56 -04:00
parent d06ad19eb0
commit 52dc4e9a03
2 changed files with 124 additions and 95 deletions

View file

@ -36,7 +36,7 @@ const ROC_FILE_EXTENSION: &str = "roc";
const MODULE_SEPARATOR: char = '.'; const MODULE_SEPARATOR: char = '.';
#[derive(Debug)] #[derive(Debug)]
pub struct LoadedModule { pub struct LoadedModule<'a> {
pub module_id: ModuleId, pub module_id: ModuleId,
pub interns: Interns, pub interns: Interns,
pub solved: Solved<Subs>, pub solved: Solved<Subs>,
@ -44,7 +44,7 @@ pub struct LoadedModule {
pub type_problems: Vec<solve::TypeError>, pub type_problems: Vec<solve::TypeError>,
pub declarations_by_id: MutMap<ModuleId, Vec<Declaration>>, pub declarations_by_id: MutMap<ModuleId, Vec<Declaration>>,
pub exposed_vars_by_symbol: Vec<(Symbol, Variable)>, pub exposed_vars_by_symbol: Vec<(Symbol, Variable)>,
pub src: Box<str>, pub src: &'a str,
} }
#[derive(Debug)] #[derive(Debug)]
@ -85,6 +85,7 @@ enum Msg<'a> {
}, },
Finished { Finished {
solved: Solved<Subs>, solved: Solved<Subs>,
problems: Vec<solve::TypeError>,
exposed_vars_by_symbol: Vec<(Symbol, Variable)>, exposed_vars_by_symbol: Vec<(Symbol, Variable)>,
src: &'a str, src: &'a str,
}, },
@ -161,15 +162,15 @@ type MsgReceiver<'a> = Receiver<Msg<'a>>;
/// specializations, so if none of their specializations changed, we don't even need /// specializations, so if none of their specializations changed, we don't even need
/// to rebuild the module and can link in the cached one directly.) /// to rebuild the module and can link in the cached one directly.)
#[allow(clippy::cognitive_complexity)] #[allow(clippy::cognitive_complexity)]
pub fn load( pub fn load<'a>(
arena: &'a Bump,
stdlib: &StdLib, stdlib: &StdLib,
src_dir: PathBuf, src_dir: PathBuf,
filename: PathBuf, filename: PathBuf,
exposed_types: SubsByModule, exposed_types: SubsByModule,
) -> Result<LoadedModule, LoadingProblem> { ) -> Result<LoadedModule<'a>, LoadingProblem> {
use self::MaybeShared::*; use self::MaybeShared::*;
let arena = Bump::new();
let (msg_tx, msg_rx) = bounded(1024); let (msg_tx, msg_rx) = bounded(1024);
let arc_modules = Arc::new(Mutex::new(ModuleIds::default())); let arc_modules = Arc::new(Mutex::new(ModuleIds::default()));
let root_exposed_ident_ids = IdentIds::exposed_builtins(0); let root_exposed_ident_ids = IdentIds::exposed_builtins(0);
@ -278,7 +279,7 @@ fn load_deps<'a>(
arc_modules: Arc<Mutex<ModuleIds>>, arc_modules: Arc<Mutex<ModuleIds>>,
ident_ids_by_module: Arc<Mutex<IdentIdsByModule>>, ident_ids_by_module: Arc<Mutex<IdentIdsByModule>>,
exposed_types: SubsByModule, exposed_types: SubsByModule,
) -> Result<LoadedModule, LoadingProblem> { ) -> Result<LoadedModule<'a>, LoadingProblem> {
// Reserve one CPU for the main thread, and let all the others be eligible // Reserve one CPU for the main thread, and let all the others be eligible
// to spawn workers. // to spawn workers.
let num_workers = num_cpus::get() - 1; let num_workers = num_cpus::get() - 1;
@ -409,6 +410,7 @@ fn load_deps<'a>(
match msg { match msg {
Msg::Finished { Msg::Finished {
solved, solved,
problems,
exposed_vars_by_symbol, exposed_vars_by_symbol,
src, src,
} => { } => {
@ -417,6 +419,8 @@ fn load_deps<'a>(
dbg!("TODO send Shutdown messages to all the worker threads."); dbg!("TODO send Shutdown messages to all the worker threads.");
state.type_problems.extend(problems);
let module_ids = Arc::try_unwrap(state.arc_modules) let module_ids = Arc::try_unwrap(state.arc_modules)
.unwrap_or_else(|_| { .unwrap_or_else(|_| {
panic!("There were still outstanding Arc references to module_ids") panic!("There were still outstanding Arc references to module_ids")
@ -762,19 +766,22 @@ fn update<'a>(
solved_module, solved_module,
solved_subs, solved_subs,
} => { } => {
state.type_problems.extend(solved_module.problems);
if module_id == state.root_id { if module_id == state.root_id {
let solved = Arc::try_unwrap(solved_subs).unwrap_or_else(|_| { let solved = Arc::try_unwrap(solved_subs).unwrap_or_else(|_| {
panic!("There were still outstanding Arc references to Solved<Subs>") panic!("There were still outstanding Arc references to Solved<Subs>")
}); });
msg_tx.send(Msg::Finished { msg_tx
.send(Msg::Finished {
solved, solved,
problems: solved_module.problems,
exposed_vars_by_symbol: solved_module.exposed_vars_by_symbol, exposed_vars_by_symbol: solved_module.exposed_vars_by_symbol,
src, src,
}); })
.map_err(|_| LoadingProblem::MsgChannelDied)?;
} else { } else {
state.type_problems.extend(solved_module.problems);
// This was a dependency. Write it down and keep processing messages. // This was a dependency. Write it down and keep processing messages.
debug_assert!(!state.exposed_types.contains_key(&module_id)); debug_assert!(!state.exposed_types.contains_key(&module_id));
state.exposed_types.insert( state.exposed_types.insert(
@ -1147,84 +1154,84 @@ fn add_exposed_to_scope(
} }
} }
//// TODO trim down these arguments - possibly by moving Constraint into Module // TODO trim down these arguments - possibly by moving Constraint into Module
//#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
//fn spawn_solve_module( fn spawn_solve_module(
// module: Module, module: Module,
// src: Box<str>, src: Box<str>,
// constraint: Constraint, constraint: Constraint,
// mut var_store: VarStore, mut var_store: VarStore,
// imported_modules: MutSet<ModuleId>, imported_modules: MutSet<ModuleId>,
// msg_tx: MsgSender, msg_tx: MsgSender,
// exposed_types: &mut SubsByModule, exposed_types: &mut SubsByModule,
// stdlib: &StdLib, stdlib: &StdLib,
//) { ) {
// let home = module.module_id; let home = module.module_id;
// // Get the constraints for this module's imports. We do this on the main thread // Get the constraints for this module's imports. We do this on the main thread
// // to avoid having to lock the map of exposed types, or to clone it // to avoid having to lock the map of exposed types, or to clone it
// // (which would be more expensive for the main thread). // (which would be more expensive for the main thread).
// let ConstrainableImports { let ConstrainableImports {
// imported_symbols, imported_symbols,
// imported_aliases, imported_aliases,
// unused_imports, unused_imports,
// } = pre_constrain_imports( } = pre_constrain_imports(
// home, home,
// &module.references, &module.references,
// imported_modules, imported_modules,
// exposed_types, exposed_types,
// stdlib, stdlib,
// ); );
// for unused_import in unused_imports { for unused_import in unused_imports {
// todo!( todo!(
// "TODO gracefully handle unused import {:?} from module {:?}", "TODO gracefully handle unused import {:?} from module {:?}",
// unused_import, unused_import,
// home home
// ); );
// } }
// // We can't pass the reference to stdlib to the thread, but we can pass mode. // We can't pass the reference to stdlib to the thread, but we can pass mode.
// let mode = stdlib.mode; let mode = stdlib.mode;
// // Start solving this module in the background. // Start solving this module in the background.
// thread_scope.spawn(move |_| { // thread_scope.spawn(move |_| {
// // Rebuild the aliases in this thread, so we don't have to clone all of // // Rebuild the aliases in this thread, so we don't have to clone all of
// // stdlib.aliases on the main thread. // // stdlib.aliases on the main thread.
// let aliases = match mode { // let aliases = match mode {
// Mode::Standard => roc_builtins::std::aliases(), // Mode::Standard => roc_builtins::std::aliases(),
// Mode::Uniqueness => roc_builtins::unique::aliases(), // Mode::Uniqueness => roc_builtins::unique::aliases(),
// }; // };
// // Finish constraining the module by wrapping the existing Constraint // // Finish constraining the module by wrapping the existing Constraint
// // in the ones we just computed. We can do this off the main thread. // // in the ones we just computed. We can do this off the main thread.
// let constraint = constrain_imports( // let constraint = constrain_imports(
// imported_symbols, // imported_symbols,
// imported_aliases, // imported_aliases,
// constraint, // constraint,
// &mut var_store, // &mut var_store,
// ); // );
// let mut constraint = load_builtin_aliases(aliases, constraint, &mut var_store); // let mut constraint = load_builtin_aliases(aliases, constraint, &mut var_store);
// // Turn Apply into Alias // // Turn Apply into Alias
// constraint.instantiate_aliases(&mut var_store); // constraint.instantiate_aliases(&mut var_store);
// let (solved_subs, solved_module) = // let (solved_subs, solved_module) =
// roc_solve::module::solve_module(module, constraint, var_store); // roc_solve::module::solve_module(module, constraint, var_store);
// thread_scope.spawn(move |_| { // thread_scope.spawn(move |_| {
// // Send the subs to the main thread for processing, // // Send the subs to the main thread for processing,
// msg_tx // msg_tx
// .send(Msg::Solved { // .send(Msg::Solved {
// src, // src,
// module_id: home, // module_id: home,
// solved_subs: Arc::new(solved_subs), // solved_subs: Arc::new(solved_subs),
// solved_module, // solved_module,
// }) // })
// .unwrap_or_else(|_| panic!("Failed to send Solved message")); // .unwrap_or_else(|_| panic!("Failed to send Solved message"));
// }); // });
// }); // });
//} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn build_parse_and_constrain_task<'a, 'b>( fn build_parse_and_constrain_task<'a, 'b>(

View file

@ -14,6 +14,7 @@ mod helpers;
#[cfg(test)] #[cfg(test)]
mod test_load { mod test_load {
use crate::helpers::fixtures_dir; use crate::helpers::fixtures_dir;
use bumpalo::Bump;
use inlinable_string::InlinableString; use inlinable_string::InlinableString;
use roc_can::def::Declaration::*; use roc_can::def::Declaration::*;
use roc_can::def::Def; use roc_can::def::Def;
@ -27,14 +28,16 @@ mod test_load {
// HELPERS // HELPERS
fn load_fixture( fn load_fixture<'a>(
arena: &'a Bump,
dir_name: &str, dir_name: &str,
module_name: &str, module_name: &str,
subs_by_module: SubsByModule, subs_by_module: SubsByModule,
) -> LoadedModule { ) -> LoadedModule<'a> {
let src_dir = fixtures_dir().join(dir_name); let src_dir = fixtures_dir().join(dir_name);
let filename = src_dir.join(format!("{}.roc", module_name)); let filename = src_dir.join(format!("{}.roc", module_name));
let loaded = load( let loaded = load(
&arena,
&roc_builtins::std::standard_stdlib(), &roc_builtins::std::standard_stdlib(),
src_dir, src_dir,
filename, filename,
@ -128,7 +131,9 @@ mod test_load {
let subs_by_module = MutMap::default(); let subs_by_module = MutMap::default();
let src_dir = fixtures_dir().join("interface_with_deps"); let src_dir = fixtures_dir().join("interface_with_deps");
let filename = src_dir.join("Primary.roc"); let filename = src_dir.join("Primary.roc");
let arena = Bump::new();
let loaded = load( let loaded = load(
&arena,
&roc_builtins::std::standard_stdlib(), &roc_builtins::std::standard_stdlib(),
src_dir, src_dir,
filename, filename,
@ -160,8 +165,9 @@ mod test_load {
#[test] #[test]
fn load_unit() { fn load_unit() {
let arena = Bump::new();
let subs_by_module = MutMap::default(); let subs_by_module = MutMap::default();
let loaded_module = load_fixture("no_deps", "Unit", subs_by_module); let loaded_module = load_fixture(&arena, "no_deps", "Unit", subs_by_module);
expect_types( expect_types(
loaded_module, loaded_module,
@ -173,8 +179,10 @@ mod test_load {
#[test] #[test]
fn import_alias() { fn import_alias() {
let arena = Bump::new();
let subs_by_module = MutMap::default(); let subs_by_module = MutMap::default();
let loaded_module = load_fixture("interface_with_deps", "ImportAlias", subs_by_module); let loaded_module =
load_fixture(&arena, "interface_with_deps", "ImportAlias", subs_by_module);
expect_types( expect_types(
loaded_module, loaded_module,
@ -186,8 +194,14 @@ mod test_load {
#[test] #[test]
fn load_and_typecheck() { fn load_and_typecheck() {
let arena = Bump::new();
let subs_by_module = MutMap::default(); let subs_by_module = MutMap::default();
let loaded_module = load_fixture("interface_with_deps", "WithBuiltins", subs_by_module); let loaded_module = load_fixture(
&arena,
"interface_with_deps",
"WithBuiltins",
subs_by_module,
);
expect_types( expect_types(
loaded_module, loaded_module,
@ -206,8 +220,10 @@ mod test_load {
#[test] #[test]
fn iface_quicksort() { fn iface_quicksort() {
let arena = Bump::new();
let subs_by_module = MutMap::default(); let subs_by_module = MutMap::default();
let loaded_module = load_fixture("interface_with_deps", "Quicksort", subs_by_module); let loaded_module =
load_fixture(&arena, "interface_with_deps", "Quicksort", subs_by_module);
expect_types( expect_types(
loaded_module, loaded_module,
@ -221,8 +237,9 @@ mod test_load {
#[test] #[test]
fn app_quicksort() { fn app_quicksort() {
let arena = Bump::new();
let subs_by_module = MutMap::default(); let subs_by_module = MutMap::default();
let loaded_module = load_fixture("app_with_deps", "Quicksort", subs_by_module); let loaded_module = load_fixture(&arena, "app_with_deps", "Quicksort", subs_by_module);
expect_types( expect_types(
loaded_module, loaded_module,
@ -236,8 +253,9 @@ mod test_load {
#[test] #[test]
fn load_astar() { fn load_astar() {
let arena = Bump::new();
let subs_by_module = MutMap::default(); let subs_by_module = MutMap::default();
let loaded_module = load_fixture("interface_with_deps", "AStar", subs_by_module); let loaded_module = load_fixture(&arena, "interface_with_deps", "AStar", subs_by_module);
expect_types( expect_types(
loaded_module, loaded_module,
@ -254,8 +272,9 @@ mod test_load {
#[test] #[test]
fn load_principal_types() { fn load_principal_types() {
let arena = Bump::new();
let subs_by_module = MutMap::default(); let subs_by_module = MutMap::default();
let loaded_module = load_fixture("no_deps", "Principal", subs_by_module); let loaded_module = load_fixture(&arena, "no_deps", "Principal", subs_by_module);
expect_types( expect_types(
loaded_module, loaded_module,
@ -268,8 +287,9 @@ mod test_load {
#[test] #[test]
fn iface_dep_types() { fn iface_dep_types() {
let arena = Bump::new();
let subs_by_module = MutMap::default(); let subs_by_module = MutMap::default();
let loaded_module = load_fixture("interface_with_deps", "Primary", subs_by_module); let loaded_module = load_fixture(&arena, "interface_with_deps", "Primary", subs_by_module);
expect_types( expect_types(
loaded_module, loaded_module,
@ -290,8 +310,9 @@ mod test_load {
#[test] #[test]
fn app_dep_types() { fn app_dep_types() {
let arena = Bump::new();
let subs_by_module = MutMap::default(); let subs_by_module = MutMap::default();
let loaded_module = load_fixture("app_with_deps", "Primary", subs_by_module); let loaded_module = load_fixture(&arena, "app_with_deps", "Primary", subs_by_module);
expect_types( expect_types(
loaded_module, loaded_module,
@ -312,8 +333,9 @@ mod test_load {
#[test] #[test]
fn imported_dep_regression() { fn imported_dep_regression() {
let arena = Bump::new();
let subs_by_module = MutMap::default(); let subs_by_module = MutMap::default();
let loaded_module = load_fixture("interface_with_deps", "OneDep", subs_by_module); let loaded_module = load_fixture(&arena, "interface_with_deps", "OneDep", subs_by_module);
expect_types( expect_types(
loaded_module, loaded_module,