mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 15:21:12 +00:00
use modules in the repl
This commit is contained in:
parent
3843c4c458
commit
cb9ce5e8d7
4 changed files with 116 additions and 109 deletions
207
cli/src/repl.rs
207
cli/src/repl.rs
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue