mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 22:34:45 +00:00
Merge pull request #829 from rtfeldman/functions-in-repl
Functions in repl
This commit is contained in:
commit
16df2c8bcb
5 changed files with 138 additions and 47 deletions
|
@ -20,6 +20,10 @@ struct Env<'a, 'env> {
|
|||
home: ModuleId,
|
||||
}
|
||||
|
||||
pub enum ToAstProblem {
|
||||
FunctionLayout,
|
||||
}
|
||||
|
||||
/// JIT execute the given main function, and then wrap its results in an Expr
|
||||
/// so we can display them to the user using the formatter.
|
||||
///
|
||||
|
@ -39,7 +43,7 @@ pub unsafe fn jit_to_ast<'a>(
|
|||
home: ModuleId,
|
||||
subs: &Subs,
|
||||
ptr_bytes: u32,
|
||||
) -> Expr<'a> {
|
||||
) -> Result<Expr<'a>, ToAstProblem> {
|
||||
let env = Env {
|
||||
arena,
|
||||
subs,
|
||||
|
@ -57,55 +61,57 @@ fn jit_to_ast_help<'a>(
|
|||
main_fn_name: &str,
|
||||
layout: &Layout<'a>,
|
||||
content: &Content,
|
||||
) -> Expr<'a> {
|
||||
) -> Result<Expr<'a>, ToAstProblem> {
|
||||
match layout {
|
||||
Layout::Builtin(Builtin::Int1) => {
|
||||
run_jit_function!(lib, main_fn_name, bool, |num| bool_to_ast(
|
||||
env, num, content
|
||||
))
|
||||
}
|
||||
Layout::Builtin(Builtin::Int1) => Ok(run_jit_function!(lib, main_fn_name, bool, |num| {
|
||||
bool_to_ast(env, num, content)
|
||||
})),
|
||||
Layout::Builtin(Builtin::Int8) => {
|
||||
Ok(
|
||||
// NOTE: this is does not handle 8-bit numbers yet
|
||||
run_jit_function!(lib, main_fn_name, u8, |num| byte_to_ast(env, num, content))
|
||||
run_jit_function!(lib, main_fn_name, u8, |num| byte_to_ast(env, num, content)),
|
||||
)
|
||||
}
|
||||
Layout::Builtin(Builtin::Int64) => {
|
||||
run_jit_function!(lib, main_fn_name, i64, |num| num_to_ast(
|
||||
Ok(run_jit_function!(lib, main_fn_name, i64, |num| num_to_ast(
|
||||
env,
|
||||
i64_to_ast(env.arena, num),
|
||||
content
|
||||
))
|
||||
)))
|
||||
}
|
||||
Layout::Builtin(Builtin::Float64) => {
|
||||
run_jit_function!(lib, main_fn_name, f64, |num| num_to_ast(
|
||||
Ok(run_jit_function!(lib, main_fn_name, f64, |num| num_to_ast(
|
||||
env,
|
||||
f64_to_ast(env.arena, num),
|
||||
content
|
||||
))
|
||||
)))
|
||||
}
|
||||
Layout::Builtin(Builtin::Str) | Layout::Builtin(Builtin::EmptyStr) => {
|
||||
Layout::Builtin(Builtin::Str) | Layout::Builtin(Builtin::EmptyStr) => Ok(
|
||||
run_jit_function!(lib, main_fn_name, &'static str, |string: &'static str| {
|
||||
str_to_ast(env.arena, env.arena.alloc(string))
|
||||
})
|
||||
}
|
||||
}),
|
||||
),
|
||||
Layout::Builtin(Builtin::EmptyList) => {
|
||||
run_jit_function!(lib, main_fn_name, &'static str, |_| { Expr::List(&[]) })
|
||||
Ok(run_jit_function!(lib, main_fn_name, &'static str, |_| {
|
||||
Expr::List(&[])
|
||||
}))
|
||||
}
|
||||
Layout::Builtin(Builtin::List(_, elem_layout)) => run_jit_function!(
|
||||
Layout::Builtin(Builtin::List(_, elem_layout)) => Ok(run_jit_function!(
|
||||
lib,
|
||||
main_fn_name,
|
||||
(*const u8, usize),
|
||||
|(ptr, len): (*const u8, usize)| { list_to_ast(env, ptr, len, elem_layout, content) }
|
||||
),
|
||||
)),
|
||||
Layout::Builtin(other) => {
|
||||
todo!("add support for rendering builtin {:?} to the REPL", other)
|
||||
}
|
||||
Layout::PhantomEmptyStruct => run_jit_function!(lib, main_fn_name, &u8, |_| {
|
||||
Layout::PhantomEmptyStruct => Ok(run_jit_function!(lib, main_fn_name, &u8, |_| {
|
||||
Expr::Record {
|
||||
update: None,
|
||||
fields: &[],
|
||||
final_comments: env.arena.alloc([]),
|
||||
}
|
||||
}),
|
||||
})),
|
||||
Layout::Struct(field_layouts) => {
|
||||
let ptr_to_ast = |ptr: *const u8| match content {
|
||||
Content::Structure(FlatType::Record(fields, _)) => {
|
||||
|
@ -134,12 +140,12 @@ fn jit_to_ast_help<'a>(
|
|||
|
||||
let result_stack_size = layout.stack_size(env.ptr_bytes);
|
||||
|
||||
run_jit_function_dynamic_type!(
|
||||
Ok(run_jit_function_dynamic_type!(
|
||||
lib,
|
||||
main_fn_name,
|
||||
result_stack_size as usize,
|
||||
|bytes: *const u8| { ptr_to_ast(bytes as *const u8) }
|
||||
)
|
||||
))
|
||||
}
|
||||
Layout::Union(union_layouts) => match content {
|
||||
Content::Structure(FlatType::TagUnion(tags, _)) => {
|
||||
|
@ -153,7 +159,7 @@ fn jit_to_ast_help<'a>(
|
|||
let size = layout.stack_size(env.ptr_bytes);
|
||||
match union_variant {
|
||||
UnionVariant::Wrapped(tags_and_layouts) => {
|
||||
run_jit_function_dynamic_type!(
|
||||
Ok(run_jit_function_dynamic_type!(
|
||||
lib,
|
||||
main_fn_name,
|
||||
size as usize,
|
||||
|
@ -181,7 +187,7 @@ fn jit_to_ast_help<'a>(
|
|||
|
||||
Expr::Apply(loc_tag_expr, output, CalledVia::Space)
|
||||
}
|
||||
)
|
||||
))
|
||||
}
|
||||
_ => unreachable!("any other variant would have a different layout"),
|
||||
}
|
||||
|
@ -201,7 +207,7 @@ fn jit_to_ast_help<'a>(
|
|||
}
|
||||
|
||||
Layout::FunctionPointer(_, _) | Layout::Closure(_, _, _) => {
|
||||
todo!("add support for rendering functions in the REPL")
|
||||
Err(ToAstProblem::FunctionLayout)
|
||||
}
|
||||
Layout::Pointer(_) => todo!("add support for rendering pointers in the REPL"),
|
||||
}
|
||||
|
|
|
@ -131,11 +131,15 @@ pub fn gen_and_eval(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<R
|
|||
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 (_, main_fn_layout) = match procedures.keys().find(|(s, _)| *s == main_fn_symbol) {
|
||||
Some(layout) => layout.clone(),
|
||||
None => {
|
||||
return Ok(ReplOutput::NoProblems {
|
||||
expr: "<function>".to_string(),
|
||||
expr_type: expr_type_str,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let ptr_bytes = target.pointer_width().unwrap().bytes() as u32;
|
||||
|
||||
|
@ -249,7 +253,7 @@ pub fn gen_and_eval(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<R
|
|||
|
||||
let lib = module_to_dylib(&env.module, &target, opt_level)
|
||||
.expect("Error loading compiled dylib for test");
|
||||
let answer = unsafe {
|
||||
let res_answer = unsafe {
|
||||
eval::jit_to_ast(
|
||||
&arena,
|
||||
lib,
|
||||
|
@ -264,7 +268,15 @@ pub fn gen_and_eval(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<R
|
|||
};
|
||||
let mut expr = bumpalo::collections::String::new_in(&arena);
|
||||
|
||||
use eval::ToAstProblem::*;
|
||||
match res_answer {
|
||||
Ok(answer) => {
|
||||
answer.format_with_options(&mut expr, Parens::NotNeeded, Newlines::Yes, 0);
|
||||
}
|
||||
Err(FunctionLayout) => {
|
||||
expr.push_str("<function>");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ReplOutput::NoProblems {
|
||||
expr: expr.into_bump_str().to_string(),
|
||||
|
|
|
@ -462,6 +462,40 @@ mod repl_eval {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn identity_lambda() {
|
||||
// Even though this gets unwrapped at runtime, the repl should still
|
||||
// report it as a record
|
||||
expect_success("\\x -> x", "<function> : a -> a");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stdlib_function() {
|
||||
// Even though this gets unwrapped at runtime, the repl should still
|
||||
// report it as a record
|
||||
expect_success("Num.abs", "<function> : Num a -> Num a");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn too_few_args() {
|
||||
expect_failure(
|
||||
"Num.add 2",
|
||||
indoc!(
|
||||
r#"
|
||||
── TOO FEW ARGS ────────────────────────────────────────────────────────────────
|
||||
|
||||
The add function expects 2 arguments, but it got only 1:
|
||||
|
||||
4│ Num.add 2
|
||||
^^^^^^^
|
||||
|
||||
Roc does not allow functions to be partially applied. Use a closure to
|
||||
make partial application explicit.
|
||||
"#
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn type_problem() {
|
||||
expect_failure(
|
||||
|
|
|
@ -20,7 +20,7 @@ use roc_module::symbol::{
|
|||
use roc_mono::ir::{
|
||||
CapturedSymbols, ExternalSpecializations, PartialProc, PendingSpecialization, Proc, Procs,
|
||||
};
|
||||
use roc_mono::layout::{Layout, LayoutCache};
|
||||
use roc_mono::layout::{Layout, LayoutCache, LayoutProblem};
|
||||
use roc_parse::ast::{self, Attempting, StrLiteral, TypeAnnotation};
|
||||
use roc_parse::header::{
|
||||
ExposesEntry, ImportsEntry, PackageEntry, PackageOrPath, PlatformHeader, To, TypedIdent,
|
||||
|
@ -3508,9 +3508,20 @@ fn add_def_to_module<'a>(
|
|||
mono_env.subs,
|
||||
) {
|
||||
Ok(l) => l,
|
||||
Err(err) => {
|
||||
// a host-exposed function is not monomorphized
|
||||
todo!("The host-exposed function {:?} does not have a valid layout (e.g. maybe the function wasn't monomorphic): {:?}", symbol, err)
|
||||
Err(LayoutProblem::Erroneous) => {
|
||||
let message = "top level function has erroneous type";
|
||||
procs.runtime_errors.insert(symbol, message);
|
||||
return;
|
||||
}
|
||||
Err(LayoutProblem::UnresolvedTypeVar(v)) => {
|
||||
let message = format!(
|
||||
"top level function has unresolved type variable {:?}",
|
||||
v
|
||||
);
|
||||
procs
|
||||
.runtime_errors
|
||||
.insert(symbol, mono_env.arena.alloc(message));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -3542,9 +3553,29 @@ fn add_def_to_module<'a>(
|
|||
// get specialized!
|
||||
if is_exposed {
|
||||
let annotation = def.expr_var;
|
||||
let layout = layout_cache.from_var(mono_env.arena, annotation, mono_env.subs).unwrap_or_else(|err|
|
||||
todo!("TODO gracefully handle the situation where we expose a function to the host which doesn't have a valid layout (e.g. maybe the function wasn't monomorphic): {:?}", err)
|
||||
|
||||
let layout = match layout_cache.from_var(
|
||||
mono_env.arena,
|
||||
annotation,
|
||||
mono_env.subs,
|
||||
) {
|
||||
Ok(l) => l,
|
||||
Err(LayoutProblem::Erroneous) => {
|
||||
let message = "top level function has erroneous type";
|
||||
procs.runtime_errors.insert(symbol, message);
|
||||
return;
|
||||
}
|
||||
Err(LayoutProblem::UnresolvedTypeVar(v)) => {
|
||||
let message = format!(
|
||||
"top level function has unresolved type variable {:?}",
|
||||
v
|
||||
);
|
||||
procs
|
||||
.runtime_errors
|
||||
.insert(symbol, mono_env.arena.alloc(message));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
procs.insert_exposed(
|
||||
symbol,
|
||||
|
|
|
@ -1508,7 +1508,6 @@ pub fn specialize_all<'a>(
|
|||
));
|
||||
|
||||
procs.runtime_errors.insert(name, error_msg);
|
||||
panic!("failed to specialize {:?}", name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4869,9 +4868,7 @@ fn reuse_function_symbol<'a>(
|
|||
// this symbol is a function, that is used by-name (e.g. as an argument to another
|
||||
// function). Register it with the current variable, then create a function pointer
|
||||
// to it in the IR.
|
||||
let layout = layout_cache
|
||||
.from_var(env.arena, arg_var, env.subs)
|
||||
.expect("creating layout does not fail");
|
||||
let res_layout = layout_cache.from_var(env.arena, arg_var, env.subs);
|
||||
|
||||
// we have three kinds of functions really. Plain functions, closures by capture,
|
||||
// and closures by unification. Here we record whether this function captures
|
||||
|
@ -4879,8 +4876,8 @@ fn reuse_function_symbol<'a>(
|
|||
let captures = partial_proc.captured_symbols.captures();
|
||||
let captured = partial_proc.captured_symbols.clone();
|
||||
|
||||
match layout {
|
||||
Layout::Closure(argument_layouts, closure_layout, ret_layout) if captures => {
|
||||
match res_layout {
|
||||
Ok(Layout::Closure(argument_layouts, closure_layout, ret_layout)) if captures => {
|
||||
// this is a closure by capture, meaning it itself captures local variables.
|
||||
// we've defined the closure as a (function_ptr, closure_data) pair already
|
||||
|
||||
|
@ -4958,7 +4955,7 @@ fn reuse_function_symbol<'a>(
|
|||
|
||||
stmt
|
||||
}
|
||||
_ => {
|
||||
Ok(layout) => {
|
||||
procs.insert_passed_by_name(
|
||||
env,
|
||||
arg_var,
|
||||
|
@ -4974,6 +4971,17 @@ fn reuse_function_symbol<'a>(
|
|||
env.arena.alloc(result),
|
||||
)
|
||||
}
|
||||
Err(LayoutProblem::Erroneous) => {
|
||||
let message = format!("The {:?} symbol has an erroneous type", symbol);
|
||||
Stmt::RuntimeError(env.arena.alloc(message))
|
||||
}
|
||||
Err(LayoutProblem::UnresolvedTypeVar(v)) => {
|
||||
let message = format!(
|
||||
"The {:?} symbol contains a unresolved type var {:?}",
|
||||
symbol, v
|
||||
);
|
||||
Stmt::RuntimeError(env.arena.alloc(message))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue