use modules in the repl

This commit is contained in:
Folkert 2020-10-14 17:48:56 +02:00
parent 3843c4c458
commit cb9ce5e8d7
4 changed files with 116 additions and 109 deletions

View file

@ -29,7 +29,7 @@ use roc_types::subs::{Content, Subs, VarStore, Variable};
use roc_types::types::Type;
use std::hash::Hash;
use std::io::{self, Write};
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use std::str::from_utf8_unchecked;
use target_lexicon::Triple;
@ -158,33 +158,64 @@ pub fn repl_home() -> ModuleId {
ModuleIds::default().get_or_insert(&"REPL".into())
}
fn promote_expr_to_module(src: &str) -> String {
let mut buffer = String::from("app Repl provides [ replOutput ] imports []\n\nreplOutput =\n");
for line in src.lines() {
// indent the body!
buffer.push_str(" ");
buffer.push_str(line);
buffer.push('\n');
}
buffer
}
fn gen(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<ReplOutput, Fail> {
use roc_reporting::report::{can_problem, type_problem, RocDocAllocator, DEFAULT_PALETTE};
// Look up the types and expressions of the `provided` values
let ptr_bytes = target.pointer_width().unwrap().bytes() as u32;
let arena = Bump::new();
let CanExprOut {
loc_expr,
var_store,
var,
constraint,
home,
interns,
problems: can_problems,
..
} = can_expr(src)?; // IMPORTANT: we must bail out here if there were UTF-8 errors!
let subs = Subs::new(var_store.into());
let mut type_problems = Vec::new();
let (content, mut subs) = infer_expr(subs, &mut type_problems, &constraint, var);
let total_problems = can_problems.len() + type_problems.len();
if total_problems > 0 {
// There were problems; report them and return.
// SAFETY: we've already verified that this is valid UTF-8 during parsing.
let src_lines: Vec<&str> = unsafe { from_utf8_unchecked(src).split('\n').collect() };
let src_str: &str = unsafe { from_utf8_unchecked(src) };
let stdlib = roc_builtins::std::standard_stdlib();
let stdlib_mode = stdlib.mode;
let filename = PathBuf::from("REPL.roc");
let src_dir = Path::new("fake/test/path");
let module_src = promote_expr_to_module(src_str);
let exposed_types = MutMap::default();
let loaded = roc_load::file::load_and_monomorphize_from_str(
&arena,
filename,
&module_src,
stdlib,
src_dir,
exposed_types,
);
let loaded = loaded.expect("failed to load module");
use roc_load::file::MonomorphizedModule;
let MonomorphizedModule {
can_problems,
type_problems,
mono_problems,
mut procedures,
interns,
exposed_to_host,
mut subs,
module_id: home,
..
} = loaded;
let error_count = can_problems.len() + type_problems.len() + mono_problems.len();
if error_count > 0 {
// There were problems; report them and return.
let src_lines: Vec<&str> = src_str.split('\n').collect();
// Used for reporting where an error came from.
//
@ -197,7 +228,7 @@ fn gen(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<ReplOutput, Fa
// Report parsing and canonicalization problems
let alloc = RocDocAllocator::new(&src_lines, home, &interns);
let mut lines = Vec::with_capacity(total_problems);
let mut lines = Vec::with_capacity(error_count);
for problem in can_problems.into_iter() {
let report = can_problem(&alloc, path.clone(), problem);
@ -223,30 +254,41 @@ fn gen(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<ReplOutput, Fa
let module = arena.alloc(roc_gen::llvm::build::module_from_builtins(&context, "app"));
let builder = context.create_builder();
// pretty-print the expr type string for later.
name_all_type_vars(var, &mut subs);
debug_assert_eq!(exposed_to_host.len(), 1);
let (main_fn_symbol, main_fn_var) = exposed_to_host.iter().nth(0).unwrap();
let main_fn_symbol = *main_fn_symbol;
let main_fn_var = *main_fn_var;
// pretty-print the expr type string for later.
name_all_type_vars(main_fn_var, &mut subs);
let content = subs.get(main_fn_var).content;
let expr_type_str = content_to_string(content.clone(), &subs, home, &interns);
let (_, main_fn_layout) = procedures
.keys()
.find(|(s, _)| *s == main_fn_symbol)
.unwrap()
.clone();
let target = target_lexicon::Triple::host();
let ptr_bytes = target.pointer_width().unwrap().bytes() as u32;
let opt_level = if cfg!(debug_assertions) {
roc_gen::llvm::build::OptLevel::Normal
} else {
roc_gen::llvm::build::OptLevel::Optimize
};
let module = arena.alloc(module);
let (module_pass, function_pass) =
roc_gen::llvm::build::construct_optimization_passes(module, opt_level);
// Compute main_fn_type before moving subs to Env
let main_ret_layout = Layout::new(&arena, content.clone(), &subs).unwrap_or_else(|err| {
panic!(
"Code gen error in test: could not convert Content to main_layout. Err was {:?}",
err
)
});
let execution_engine = module
.create_jit_execution_engine(OptimizationLevel::None)
.expect("Error creating JIT execution engine for test");
// Without calling this, we get a linker error when building this crate
// in --release mode and then trying to eval anything in the repl.
ExecutionEngine::link_in_mc_jit();
// Compile and add all the Procs before adding main
let mut env = roc_gen::llvm::build::Env {
let env = roc_gen::llvm::build::Env {
arena: &arena,
builder: &builder,
context: &context,
@ -254,64 +296,18 @@ fn gen(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<ReplOutput, Fa
module,
ptr_bytes,
leak: false,
// important! we don't want any procedures to get the C calling convention
exposed_to_host: MutSet::default(),
};
let mut procs = Procs::default();
let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
let mut layout_ids = LayoutIds::default();
// Populate Procs and get the low-level Expr from the canonical Expr
let mut mono_problems = Vec::new();
let mut mono_env = roc_mono::ir::Env {
arena: &arena,
subs: &mut subs,
problems: &mut mono_problems,
home,
ident_ids: &mut ident_ids,
};
let mut layout_cache = LayoutCache::default();
let main_body =
roc_mono::ir::Stmt::new(&mut mono_env, loc_expr.value, &mut procs, &mut layout_cache);
let param_map = roc_mono::borrow::ParamMap::default();
let main_body = roc_mono::inc_dec::visit_declaration(
mono_env.arena,
mono_env.arena.alloc(param_map),
mono_env.arena.alloc(main_body),
);
let mut headers = {
let num_headers = match &procs.pending_specializations {
Some(map) => map.len(),
None => 0,
};
Vec::with_capacity(num_headers)
};
let procs = roc_mono::ir::specialize_all(&mut mono_env, procs, &mut layout_cache);
assert_eq!(
procs.runtime_errors,
roc_collections::all::MutMap::default()
);
let (mut procs, param_map) = procs.get_specialized_procs_help(mono_env.arena);
let main_body = roc_mono::inc_dec::visit_declaration(
mono_env.arena,
param_map,
mono_env.arena.alloc(main_body),
);
// Put this module's ident_ids back in the interns, so we can use them in env.
// This must happen *after* building the headers, because otherwise there's
// a conflicting mutable borrow on ident_ids.
env.interns.all_ident_ids.insert(home, ident_ids);
let mut layout_ids = roc_gen::layout_id::LayoutIds::default();
let mut headers = Vec::with_capacity(procedures.len());
// Add all the Proc headers to the module.
// We have to do this in a separate pass first,
// because their bodies may reference each other.
let mut scope = roc_gen::llvm::build::Scope::default();
for ((symbol, layout), proc) in procs.drain() {
for ((symbol, layout), proc) in procedures.drain() {
let fn_val = build_proc_header(&env, &mut layout_ids, symbol, &layout, &proc);
if proc.args.is_empty() {
@ -325,34 +321,45 @@ fn gen(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<ReplOutput, Fa
// Build each proc using its header info.
for (proc, fn_val) in headers {
// NOTE: This is here to be uncommented in case verification fails.
// (This approach means we don't have to defensively clone name here.)
//
// println!("\n\nBuilding and then verifying function {}\n\n", name);
let mut current_scope = scope.clone();
// only have top-level thunks for this proc's module in scope
// this retain is not needed for correctness, but will cause less confusion when debugging
let home = proc.name.module_id();
current_scope.retain_top_level_thunks_for_module(home);
build_proc(&env, &mut layout_ids, scope.clone(), proc, fn_val);
if fn_val.verify(true) {
function_pass.run_on(&fn_val);
} else {
use roc_builtins::std::Mode;
let mode = match stdlib_mode {
Mode::Uniqueness => "OPTIMIZED",
Mode::Standard => "NON-OPTIMIZED",
};
eprintln!(
"\n\nFunction {:?} failed LLVM verification in build. Its content was:\n",
fn_val.get_name().to_str().unwrap()
"\n\nFunction {:?} failed LLVM verification in {} build. Its content was:\n",
fn_val.get_name().to_str().unwrap(),
mode,
);
fn_val.print_to_stderr();
panic!(
"The preceding code was from {:?}, which failed LLVM verification in build.",
fn_val.get_name().to_str().unwrap()
"The preceding code was from {:?}, which failed LLVM verification in {} build.",
fn_val.get_name().to_str().unwrap(),
mode,
);
}
}
let (main_fn_name, main_fn) = roc_gen::llvm::build::make_main_function(
let (main_fn_name, main_fn) = roc_gen::llvm::build::promote_to_main_function(
&env,
&mut layout_ids,
&main_ret_layout,
&main_body,
main_fn_symbol,
&main_fn_layout,
);
// Uncomment this to see the module's un-optimized LLVM instruction output:
@ -379,7 +386,7 @@ fn gen(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<ReplOutput, Fa
&arena,
execution_engine,
main_fn_name,
&main_ret_layout,
&main_fn_layout,
&content,
&env.interns,
home,

View file

@ -67,7 +67,7 @@ pub fn gen_from_mono_module(
module,
ptr_bytes,
leak: false,
exposed_to_host: loaded.exposed_to_host,
exposed_to_host: loaded.exposed_to_host.keys().copied().collect(),
};
// Populate Procs further and get the low-level Expr from the canonical Expr

View file

@ -134,8 +134,8 @@ pub fn helper<'a>(
// important! we don't want any procedures to get the C calling convention
exposed_to_host: MutSet::default(),
};
let mut layout_ids = roc_gen::layout_id::LayoutIds::default();
let mut layout_ids = roc_gen::layout_id::LayoutIds::default();
let mut headers = Vec::with_capacity(procedures.len());
// Add all the Proc headers to the module.

View file

@ -439,7 +439,7 @@ pub struct MonomorphizedModule<'a> {
pub type_problems: Vec<solve::TypeError>,
pub mono_problems: Vec<roc_mono::ir::MonoProblem>,
pub procedures: MutMap<(Symbol, Layout<'a>), Proc<'a>>,
pub exposed_to_host: MutSet<Symbol>,
pub exposed_to_host: MutMap<Symbol, Variable>,
pub src: Box<str>,
pub timings: MutMap<ModuleId, ModuleTiming>,
}
@ -498,7 +498,7 @@ enum Msg<'a> {
FinishedAllSpecialization {
subs: Subs,
problems: Vec<MonoProblem>,
exposed_to_host: MutSet<Symbol>,
exposed_to_host: MutMap<Symbol, Variable>,
src: &'a str,
},
}
@ -525,7 +525,7 @@ struct State<'a> {
pub module_cache: ModuleCache<'a>,
pub dependencies: Dependencies,
pub procedures: MutMap<(Symbol, Layout<'a>), Proc<'a>>,
pub exposed_to_host: MutSet<Symbol>,
pub exposed_to_host: MutMap<Symbol, Variable>,
/// This is the "final" list of IdentIds, after canonicalization and constraint gen
/// have completed for a given module.
@ -667,7 +667,7 @@ enum BuildTask<'a> {
ident_ids: IdentIds,
decls: Vec<Declaration>,
finished_info: FinishedInfo<'a>,
exposed_to_host: MutSet<Symbol>,
exposed_to_host: MutMap<Symbol, Variable>,
},
MakeSpecializations {
module_id: ModuleId,
@ -1060,7 +1060,7 @@ where
module_cache: ModuleCache::default(),
dependencies: Dependencies::default(),
procedures: MutMap::default(),
exposed_to_host: MutSet::default(),
exposed_to_host: MutMap::default(),
exposed_types,
headers_parsed,
loading_started,
@ -1283,7 +1283,7 @@ fn update<'a>(
if module_id == state.root_id {
state
.exposed_to_host
.extend(solved_module.exposed_vars_by_symbol.iter().map(|x| x.0));
.extend(solved_module.exposed_vars_by_symbol.iter().copied());
}
if module_id == state.root_id && state.goal_phase == Phase::SolveTypes {
@ -1480,7 +1480,7 @@ fn finish_specialization<'a>(
mut state: State<'a>,
subs: Subs,
problems: Vec<MonoProblem>,
exposed_to_host: MutSet<Symbol>,
exposed_to_host: MutMap<Symbol, Variable>,
src: &'a str,
) -> MonomorphizedModule<'a> {
state.mono_problems.extend(problems);
@ -2148,7 +2148,7 @@ fn build_pending_specializations<'a>(
_module_timing: ModuleTiming,
mut layout_cache: LayoutCache<'a>,
// TODO remove
exposed_to_host: MutSet<Symbol>,
exposed_to_host: MutMap<Symbol, Variable>,
finished_info: FinishedInfo<'a>,
) -> Msg<'a> {
let mut procs = Procs::default();
@ -2212,7 +2212,7 @@ fn add_def_to_module<'a>(
procs: &mut Procs<'a>,
mono_env: &mut roc_mono::ir::Env<'a, '_>,
def: roc_can::def::Def,
exposed_to_host: &MutSet<Symbol>,
exposed_to_host: &MutMap<Symbol, Variable>,
is_recursive: bool,
) {
use roc_can::expr::Expr::*;
@ -2220,7 +2220,7 @@ fn add_def_to_module<'a>(
match def.loc_pattern.value {
Identifier(symbol) => {
let is_exposed = exposed_to_host.contains(&symbol);
let is_exposed = exposed_to_host.contains_key(&symbol);
match def.loc_expr.value {
Closure {