From 95e0faad30f1d0d3244f76d6e56bf2bad77fa8ac Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 26 Dec 2020 21:19:45 +0100 Subject: [PATCH 1/5] show type of lambda in the repl --- cli/src/repl/gen.rs | 14 +++++++++----- compiler/load/src/file.rs | 19 +++++++++++++++---- compiler/mono/src/ir.rs | 1 - 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/cli/src/repl/gen.rs b/cli/src/repl/gen.rs index 1d9427842a..be27087b38 100644 --- a/cli/src/repl/gen.rs +++ b/cli/src/repl/gen.rs @@ -131,11 +131,15 @@ pub fn gen_and_eval(src: &[u8], target: Triple, opt_level: OptLevel) -> Result layout.clone(), + None => { + return Ok(ReplOutput::NoProblems { + expr: "".to_string(), + expr_type: expr_type_str, + }); + } + }; let ptr_bytes = target.pointer_width().unwrap().bytes() as u32; diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index fd61954feb..1425b8b921 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -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, @@ -3503,9 +3503,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; } }; diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index dc08c98176..f5e2f0b0ca 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -1508,7 +1508,6 @@ pub fn specialize_all<'a>( )); procs.runtime_errors.insert(name, error_msg); - panic!("failed to specialize {:?}", name); } } } From 3d1824c65894864b98ea036cabd5aac7e87cc137 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 26 Dec 2020 21:20:50 +0100 Subject: [PATCH 2/5] add test --- cli/tests/repl_eval.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cli/tests/repl_eval.rs b/cli/tests/repl_eval.rs index c57a8459d8..a5a792b752 100644 --- a/cli/tests/repl_eval.rs +++ b/cli/tests/repl_eval.rs @@ -462,6 +462,13 @@ 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", " : a -> a"); + } + #[test] fn type_problem() { expect_failure( From 12c7b5435ac2f5462a80c08162c5383ad58b9bdb Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 26 Dec 2020 21:30:25 +0100 Subject: [PATCH 3/5] generate more runtime errors --- cli/tests/repl_eval.rs | 7 +++++++ compiler/mono/src/ir.rs | 21 +++++++++++++++------ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/cli/tests/repl_eval.rs b/cli/tests/repl_eval.rs index a5a792b752..1bf884fcbe 100644 --- a/cli/tests/repl_eval.rs +++ b/cli/tests/repl_eval.rs @@ -469,6 +469,13 @@ mod repl_eval { expect_success("\\x -> x", " : 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", " : Num a -> Num a"); + } + #[test] fn type_problem() { expect_failure( diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index f5e2f0b0ca..22c0a14b37 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -4868,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 @@ -4878,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 @@ -4957,7 +4955,7 @@ fn reuse_function_symbol<'a>( stmt } - _ => { + Ok(layout) => { procs.insert_passed_by_name( env, arg_var, @@ -4973,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)) + } } } } From ebe22c99034cd36942dd67bb0045286b892c3005 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 26 Dec 2020 21:54:00 +0100 Subject: [PATCH 4/5] repl support for already-defined functions --- cli/src/repl/eval.rs | 58 ++++++++++++++++++++++++-------------------- cli/src/repl/gen.rs | 12 +++++++-- 2 files changed, 42 insertions(+), 28 deletions(-) diff --git a/cli/src/repl/eval.rs b/cli/src/repl/eval.rs index 4a20f0601d..5e4d07e6bc 100644 --- a/cli/src/repl/eval.rs +++ b/cli/src/repl/eval.rs @@ -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, 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, 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) => { - // 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)) + 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)), + ) } 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"), } diff --git a/cli/src/repl/gen.rs b/cli/src/repl/gen.rs index be27087b38..a67dda0ddb 100644 --- a/cli/src/repl/gen.rs +++ b/cli/src/repl/gen.rs @@ -253,7 +253,7 @@ pub fn gen_and_eval(src: &[u8], target: Triple, opt_level: OptLevel) -> Result Result { + answer.format_with_options(&mut expr, Parens::NotNeeded, Newlines::Yes, 0); + } + Err(FunctionLayout) => { + expr.push_str(""); + } + } Ok(ReplOutput::NoProblems { expr: expr.into_bump_str().to_string(), From a760a41e1f737ca4b706e9aa45259570e7b14064 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 26 Dec 2020 22:13:05 +0100 Subject: [PATCH 5/5] repl: show error for partial application --- cli/tests/repl_eval.rs | 20 ++++++++++++++++++++ compiler/load/src/file.rs | 26 +++++++++++++++++++++++--- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/cli/tests/repl_eval.rs b/cli/tests/repl_eval.rs index 1bf884fcbe..ed94b3db4c 100644 --- a/cli/tests/repl_eval.rs +++ b/cli/tests/repl_eval.rs @@ -476,6 +476,26 @@ mod repl_eval { expect_success("Num.abs", " : 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( diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index 1425b8b921..c6110801ca 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -3548,9 +3548,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,