diff --git a/compiler/can/src/env.rs b/compiler/can/src/env.rs index d896f9ea6f..b86b3f54bf 100644 --- a/compiler/can/src/env.rs +++ b/compiler/can/src/env.rs @@ -1,6 +1,7 @@ use crate::procedure::References; use inlinable_string::InlinableString; use roc_collections::all::{MutMap, MutSet}; +use roc_module::ident::ModuleName; use roc_module::symbol::{IdentIds, ModuleId, ModuleIds, Symbol}; use roc_problem::can::{Problem, RuntimeError}; use roc_region::all::{Located, Region}; @@ -113,7 +114,7 @@ impl<'a> Env<'a> { Ok(symbol) } None => Err(RuntimeError::ValueNotExposed { - module_name, + module_name: ModuleName::from(module_name), ident, region, }), diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 9215b4ff0f..bdfdc12dd9 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -2782,7 +2782,25 @@ pub fn with_hole<'a>( use crate::layout::UnionVariant::*; let arena = env.arena; - let variant = crate::layout::union_sorted_tags(env.arena, variant_var, env.subs); + let res_variant = crate::layout::union_sorted_tags(env.arena, variant_var, env.subs); + + let variant = match res_variant { + Ok(cached) => cached, + Err(LayoutProblem::UnresolvedTypeVar(_)) => { + return Stmt::RuntimeError(env.arena.alloc(format!( + "UnresolvedTypeVar {} line {}", + file!(), + line!() + ))); + } + Err(LayoutProblem::Erroneous) => { + return Stmt::RuntimeError(env.arena.alloc(format!( + "Erroneous {} line {}", + file!(), + line!() + ))); + } + }; match variant { Never => unreachable!( @@ -4581,9 +4599,23 @@ fn from_can_when<'a>( } let opt_branches = to_opt_branches(env, region, branches, layout_cache); - let cond_layout = layout_cache - .from_var(env.arena, cond_var, env.subs) - .unwrap_or_else(|err| panic!("TODO turn this into a RuntimeError {:?}", err)); + let cond_layout = match layout_cache.from_var(env.arena, cond_var, env.subs) { + Ok(cached) => cached, + Err(LayoutProblem::UnresolvedTypeVar(_)) => { + return Stmt::RuntimeError(env.arena.alloc(format!( + "UnresolvedTypeVar {} line {}", + file!(), + line!() + ))); + } + Err(LayoutProblem::Erroneous) => { + return Stmt::RuntimeError(env.arena.alloc(format!( + "Erroneous {} line {}", + file!(), + line!() + ))); + } + }; let ret_layout = layout_cache .from_var(env.arena, expr_var, env.subs) @@ -6039,7 +6071,15 @@ fn from_can_pattern_help<'a>( use crate::exhaustive::Union; use crate::layout::UnionVariant::*; - let variant = crate::layout::union_sorted_tags(env.arena, *whole_var, env.subs); + let res_variant = crate::layout::union_sorted_tags(env.arena, *whole_var, env.subs); + + let variant = match res_variant { + Ok(cached) => cached, + Err(LayoutProblem::UnresolvedTypeVar(_)) => { + return Err(RuntimeError::UnresolvedTypeVar) + } + Err(LayoutProblem::Erroneous) => return Err(RuntimeError::ErroneousType), + }; let result = match variant { Never => unreachable!( diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index a7cc364349..a2134ad95f 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -1324,7 +1324,11 @@ impl<'a> WrappedVariant<'a> { } } -pub fn union_sorted_tags<'a>(arena: &'a Bump, var: Variable, subs: &Subs) -> UnionVariant<'a> { +pub fn union_sorted_tags<'a>( + arena: &'a Bump, + var: Variable, + subs: &Subs, +) -> Result, LayoutProblem> { let var = if let Content::RecursionVar { structure, .. } = subs.get_without_compacting(var).content { structure @@ -1338,10 +1342,11 @@ pub fn union_sorted_tags<'a>(arena: &'a Bump, var: Variable, subs: &Subs) -> Uni let opt_rec_var = get_recursion_var(subs, var); union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs) } + Err((_, Content::Error)) => return Err(LayoutProblem::Erroneous), Err(other) => panic!("invalid content in tag union variable: {:?}", other), }; - result + Ok(result) } fn get_recursion_var(subs: &Subs, var: Variable) -> Option { diff --git a/compiler/problem/src/can.rs b/compiler/problem/src/can.rs index 1173630fb3..5eee364411 100644 --- a/compiler/problem/src/can.rs +++ b/compiler/problem/src/can.rs @@ -1,6 +1,6 @@ use inlinable_string::InlinableString; use roc_collections::all::MutSet; -use roc_module::ident::{Ident, Lowercase, TagName}; +use roc_module::ident::{Ident, Lowercase, ModuleName, TagName}; use roc_module::operator::BinOp; use roc_module::symbol::{ModuleId, Symbol}; use roc_parse::ast::Base; @@ -117,9 +117,13 @@ pub enum RuntimeError { UnsupportedPattern(Region), // Example: when 1 is 1.X -> 32 MalformedPattern(MalformedPatternProblem, Region), + + UnresolvedTypeVar, + ErroneousType, + LookupNotInScope(Located, MutSet>), ValueNotExposed { - module_name: InlinableString, + module_name: ModuleName, ident: InlinableString, region: Region, }, diff --git a/compiler/reporting/src/error/canonicalize.rs b/compiler/reporting/src/error/canonicalize.rs index 8c12467eb0..1c243d91e6 100644 --- a/compiler/reporting/src/error/canonicalize.rs +++ b/compiler/reporting/src/error/canonicalize.rs @@ -344,6 +344,11 @@ fn pretty_runtime_error<'b>( unreachable!("") } + RuntimeError::UnresolvedTypeVar | RuntimeError::ErroneousType => { + // only generated during layout generation + unreachable!("") + } + RuntimeError::Shadowing { original_region, shadow, @@ -443,7 +448,20 @@ fn pretty_runtime_error<'b>( RuntimeError::UnsupportedPattern(_) => { todo!("unsupported patterns are currently not parsed!") } - RuntimeError::ValueNotExposed { .. } => todo!("value not exposed"), + RuntimeError::ValueNotExposed { module_name, ident, region } => { + alloc.stack(vec![ + alloc.concat(vec![ + alloc.reflow("The "), + alloc.module_name(module_name), + alloc.reflow(" module does not expose a "), + alloc.string(ident.to_string()), + alloc.reflow(" value:"), + ]), + alloc.region(region), + ]) + } + + RuntimeError::ModuleNotImported { module_name, imported_modules, diff --git a/compiler/reporting/src/report.rs b/compiler/reporting/src/report.rs index 362a003b1b..f32dac57aa 100644 --- a/compiler/reporting/src/report.rs +++ b/compiler/reporting/src/report.rs @@ -1,6 +1,6 @@ use inlinable_string::InlinableString; use roc_module::ident::Ident; -use roc_module::ident::{Lowercase, TagName, Uppercase}; +use roc_module::ident::{Lowercase, ModuleName, TagName, Uppercase}; use roc_module::symbol::{Interns, ModuleId, Symbol}; use std::fmt; use std::path::PathBuf; @@ -316,6 +316,17 @@ impl<'a> RocDocAllocator<'a> { self.text(name).annotate(Annotation::Module) } + pub fn module_name(&'a self, name: ModuleName) -> DocBuilder<'a, Self, Annotation> { + let name = if name.is_empty() { + // Render the app module as "app" + "app".to_string() + } else { + name.as_str().to_string() + }; + + self.text(name).annotate(Annotation::Module) + } + pub fn inlinable_string(&'a self, s: InlinableString) -> DocBuilder<'a, Self, Annotation> { self.text(format!("{}", s)).annotate(Annotation::Module) } diff --git a/compiler/reporting/tests/test_reporting.rs b/compiler/reporting/tests/test_reporting.rs index aeaf14b1d8..8d281e6fa9 100644 --- a/compiler/reporting/tests/test_reporting.rs +++ b/compiler/reporting/tests/test_reporting.rs @@ -216,6 +216,27 @@ mod test_reporting { .replace(UNDERLINE_CODE, "") } + #[test] + fn value_not_exposed() { + report_problem_as( + indoc!( + r#" + List.foobar 1 2 + "# + ), + indoc!( + r#" + ── SYNTAX PROBLEM ────────────────────────────────────────────────────────────── + + The List module does not expose a foobar value: + + 1│ List.foobar 1 2 + ^^^^^^^^^^^ + "# + ), + ) + } + #[test] fn report_unused_def() { report_problem_as( diff --git a/editor/src/lang/expr.rs b/editor/src/lang/expr.rs index f9dba9e7bd..80de5aad73 100644 --- a/editor/src/lang/expr.rs +++ b/editor/src/lang/expr.rs @@ -12,6 +12,7 @@ use inlinable_string::InlinableString; use roc_can::expr::Recursive; use roc_can::num::{finish_parsing_base, finish_parsing_float, finish_parsing_int}; use roc_collections::all::{MutMap, MutSet}; +use roc_module::ident::ModuleName; use roc_module::low_level::LowLevel; use roc_module::operator::CalledVia; use roc_module::symbol::{IdentIds, ModuleId, ModuleIds, Symbol}; @@ -178,7 +179,7 @@ impl<'a> Env<'a> { Ok(symbol) } None => Err(RuntimeError::ValueNotExposed { - module_name, + module_name: ModuleName::from(module_name), ident, region, }),