diff --git a/AUTHORS b/AUTHORS index 7d5a1e502f..117f57c396 100644 --- a/AUTHORS +++ b/AUTHORS @@ -61,3 +61,4 @@ Shahn Hogan Tankor Smash Matthias Devlamynck Jan Van Bruggen +Mats Sigge <> diff --git a/Cargo.lock b/Cargo.lock index fb77fdb6cc..f98de87446 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3248,12 +3248,12 @@ dependencies = [ "roc_builtins", "roc_can", "roc_collections", + "roc_error_macros", "roc_load", "roc_module", "roc_parse", "roc_problem", "roc_region", - "roc_reporting", "roc_types", "roc_unify", "snafu", @@ -3338,6 +3338,7 @@ dependencies = [ "roc_constrain", "roc_docs", "roc_editor", + "roc_error_macros", "roc_fmt", "roc_gen_llvm", "roc_linker", @@ -3470,6 +3471,10 @@ dependencies = [ "winit", ] +[[package]] +name = "roc_error_macros" +version = "0.1.0" + [[package]] name = "roc_fmt" version = "0.1.0" @@ -3495,12 +3500,12 @@ dependencies = [ "roc_builtins", "roc_can", "roc_collections", + "roc_error_macros", "roc_module", "roc_mono", "roc_parse", "roc_problem", "roc_region", - "roc_reporting", "roc_solve", "roc_std", "roc_types", @@ -3517,9 +3522,9 @@ dependencies = [ "morphic_lib", "roc_builtins", "roc_collections", + "roc_error_macros", "roc_module", "roc_mono", - "roc_reporting", "roc_std", "target-lexicon", ] @@ -3531,9 +3536,9 @@ dependencies = [ "bumpalo", "roc_builtins", "roc_collections", + "roc_error_macros", "roc_module", "roc_mono", - "roc_reporting", "roc_std", ] diff --git a/Cargo.toml b/Cargo.toml index 234708adcb..465e29e8a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ members = [ "ast", "cli", "code_markup", + "error_macros", "reporting", "roc_std", "test_utils", diff --git a/Earthfile b/Earthfile index fdf87c0946..a1a1ce8c14 100644 --- a/Earthfile +++ b/Earthfile @@ -47,7 +47,7 @@ install-zig-llvm-valgrind-clippy-rustfmt: copy-dirs: FROM +install-zig-llvm-valgrind-clippy-rustfmt - COPY --dir cli cli_utils compiler docs editor ast code_markup utils test_utils reporting roc_std vendor examples linker Cargo.toml Cargo.lock version.txt ./ + COPY --dir cli cli_utils compiler docs editor ast code_markup error_macros utils test_utils reporting roc_std vendor examples linker Cargo.toml Cargo.lock version.txt ./ test-zig: FROM +install-zig-llvm-valgrind-clippy-rustfmt diff --git a/ast/Cargo.toml b/ast/Cargo.toml index 9c3498821c..66407ee82b 100644 --- a/ast/Cargo.toml +++ b/ast/Cargo.toml @@ -17,7 +17,7 @@ roc_problem = { path = "../compiler/problem" } roc_types = { path = "../compiler/types" } roc_unify = { path = "../compiler/unify"} roc_load = { path = "../compiler/load" } -roc_reporting = { path = "../reporting" } +roc_error_macros = { path = "../error_macros" } arrayvec = "0.7.2" bumpalo = { version = "3.8.0", features = ["collections"] } libc = "0.2.106" diff --git a/ast/src/lang/core/str.rs b/ast/src/lang/core/str.rs index 982e684629..dee65a3436 100644 --- a/ast/src/lang/core/str.rs +++ b/ast/src/lang/core/str.rs @@ -1,6 +1,6 @@ +use roc_error_macros::internal_error; use roc_module::{called_via::CalledVia, symbol::Symbol}; use roc_parse::ast::StrLiteral; -use roc_reporting::internal_error; use crate::{ ast_error::{ASTResult, UnexpectedASTNode}, diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 7b3d945425..bba74100cc 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -62,6 +62,7 @@ roc_gen_llvm = { path = "../compiler/gen_llvm", optional = true } roc_build = { path = "../compiler/build", default-features = false } roc_fmt = { path = "../compiler/fmt" } roc_reporting = { path = "../reporting" } +roc_error_macros = { path = "../error_macros" } roc_editor = { path = "../editor", optional = true } roc_linker = { path = "../linker" } clap = { version = "= 3.0.0-beta.5", default-features = false, features = ["std", "color", "suggestions"] } diff --git a/cli/src/format.rs b/cli/src/format.rs index 13f3edc71f..ea289b4647 100644 --- a/cli/src/format.rs +++ b/cli/src/format.rs @@ -2,6 +2,7 @@ use std::path::PathBuf; use bumpalo::collections::Vec; use bumpalo::Bump; +use roc_error_macros::{internal_error, user_error}; use roc_fmt::def::fmt_def; use roc_fmt::module::fmt_module; use roc_fmt::Buf; @@ -21,7 +22,6 @@ use roc_parse::{ state::State, }; use roc_region::all::{Loc, Region}; -use roc_reporting::{internal_error, user_error}; pub fn format(files: std::vec::Vec) { for file in files { diff --git a/cli/src/repl.rs b/cli/src/repl.rs index 61c10a03a5..0ca12fba55 100644 --- a/cli/src/repl.rs +++ b/cli/src/repl.rs @@ -1,7 +1,7 @@ use const_format::concatcp; #[cfg(feature = "llvm")] use gen::{gen_and_eval, ReplOutput}; -use roc_parse::parser::{EExpr, SyntaxError}; +use roc_parse::parser::{EExpr, ELambda, SyntaxError}; use rustyline::highlight::{Highlighter, PromptInfo}; use rustyline::validate::{self, ValidationContext, ValidationResult, Validator}; use rustyline_derive::{Completer, Helper, Hinter}; @@ -97,7 +97,8 @@ impl Validator for InputValidator { match roc_parse::expr::parse_loc_expr(0, &arena, state) { // Special case some syntax errors to allow for multi-line inputs Err((_, EExpr::DefMissingFinalExpr(_), _)) - | Err((_, EExpr::DefMissingFinalExpr2(_, _), _)) => { + | Err((_, EExpr::DefMissingFinalExpr2(_, _), _)) + | Err((_, EExpr::Lambda(ELambda::Body(_, _), _), _)) => { Ok(ValidationResult::Incomplete) } _ => Ok(ValidationResult::Valid(None)), diff --git a/cli/tests/repl_eval.rs b/cli/tests/repl_eval.rs index 3d36609272..5069c29f98 100644 --- a/cli/tests/repl_eval.rs +++ b/cli/tests/repl_eval.rs @@ -925,4 +925,38 @@ mod repl_eval { ), ); } + + #[test] + fn issue_2343_complete_mono_with_shadowed_vars() { + expect_failure( + indoc!( + r#" + b = False + f = \b -> + when b is + True -> 5 + False -> 15 + f b + "# + ), + indoc!( + r#" + ── DUPLICATE NAME ────────────────────────────────────────────────────────────── + + The b name is first defined here: + + 4│ b = False + ^ + + But then it's defined a second time here: + + 5│ f = \b -> + ^ + + Since these variables have the same name, it's easy to use the wrong + one on accident. Give one of them a new name. + "# + ), + ); + } } diff --git a/compiler/builtins/docs/List.roc b/compiler/builtins/docs/List.roc index 09700da429..9d6a58bdbe 100644 --- a/compiler/builtins/docs/List.roc +++ b/compiler/builtins/docs/List.roc @@ -32,6 +32,8 @@ interface List set, single, sortWith, + split, + sublist, sum, swap, walk, @@ -205,7 +207,7 @@ empty : List * ## Returns a list with the given length, where every element is the given value. ## ## -repeat : elem, Nat -> List elem +repeat : Nat, elem -> List elem ## Returns a list of all the integers between one and another, ## including both of the given numbers. @@ -277,7 +279,7 @@ map4 : List a, List b, List c, List d, (a, b, c, d -> e) -> List e ## This works like [List.map], except it also passes the index ## of the element to the conversion function. -mapWithIndex : List before, (before, Nat -> after) -> List after +mapWithIndex : List before, (Nat, before -> after) -> List after ## This works like [List.map], except at any time you can return `Err` to ## cancel the entire operation immediately, and return that #Err. diff --git a/compiler/can/src/annotation.rs b/compiler/can/src/annotation.rs index 2867a23f03..a78ed60cb9 100644 --- a/compiler/can/src/annotation.rs +++ b/compiler/can/src/annotation.rs @@ -390,7 +390,7 @@ fn can_annotation_help( ) { Ok(symbol) => symbol, - Err((original_region, shadow)) => { + Err((original_region, shadow, _new_symbol)) => { let problem = Problem::Shadowed(original_region, shadow.clone()); env.problem(roc_problem::can::Problem::ShadowingInAnnotation { diff --git a/compiler/can/src/def.rs b/compiler/can/src/def.rs index 05584133e2..43e7af2e95 100644 --- a/compiler/can/src/def.rs +++ b/compiler/can/src/def.rs @@ -809,7 +809,7 @@ fn pattern_to_vars_by_symbol( ) { use Pattern::*; match pattern { - Identifier(symbol) => { + Identifier(symbol) | Shadowed(_, _, symbol) => { vars_by_symbol.insert(*symbol, expr_var); } @@ -832,8 +832,6 @@ fn pattern_to_vars_by_symbol( | Underscore | MalformedPattern(_, _) | UnsupportedPattern(_) => {} - - Shadowed(_, _) => {} } } @@ -884,7 +882,7 @@ fn canonicalize_pending_def<'a>( Pattern::Identifier(symbol) => RuntimeError::NoImplementationNamed { def_symbol: *symbol, }, - Pattern::Shadowed(region, loc_ident) => RuntimeError::Shadowing { + Pattern::Shadowed(region, loc_ident, _new_symbol) => RuntimeError::Shadowing { original_region: *region, shadow: loc_ident.clone(), }, @@ -1502,7 +1500,7 @@ fn to_pending_def<'a>( )) } - Err((original_region, loc_shadowed_symbol)) => { + Err((original_region, loc_shadowed_symbol, _new_symbol)) => { env.problem(Problem::ShadowingInAnnotation { original_region, shadow: loc_shadowed_symbol, diff --git a/compiler/can/src/module.rs b/compiler/can/src/module.rs index 1f57cc8734..7a292807bb 100644 --- a/compiler/can/src/module.rs +++ b/compiler/can/src/module.rs @@ -398,7 +398,7 @@ fn fix_values_captured_in_closure_pattern( | FloatLiteral(_, _, _) | StrLiteral(_) | Underscore - | Shadowed(_, _) + | Shadowed(..) | MalformedPattern(_, _) | UnsupportedPattern(_) => (), } diff --git a/compiler/can/src/pattern.rs b/compiler/can/src/pattern.rs index 66132dbafb..6c9392e919 100644 --- a/compiler/can/src/pattern.rs +++ b/compiler/can/src/pattern.rs @@ -32,7 +32,7 @@ pub enum Pattern { Underscore, // Runtime Exceptions - Shadowed(Region, Loc), + Shadowed(Region, Loc, Symbol), // Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments! UnsupportedPattern(Region), // parse error patterns @@ -65,7 +65,7 @@ pub fn symbols_from_pattern_help(pattern: &Pattern, symbols: &mut Vec) { use Pattern::*; match pattern { - Identifier(symbol) => { + Identifier(symbol) | Shadowed(_, _, symbol) => { symbols.push(*symbol); } @@ -92,8 +92,6 @@ pub fn symbols_from_pattern_help(pattern: &Pattern, symbols: &mut Vec) { | Underscore | MalformedPattern(_, _) | UnsupportedPattern(_) => {} - - Shadowed(_, _) => {} } } @@ -121,13 +119,14 @@ pub fn canonicalize_pattern<'a>( Pattern::Identifier(symbol) } - Err((original_region, shadow)) => { + Err((original_region, shadow, new_symbol)) => { env.problem(Problem::RuntimeError(RuntimeError::Shadowing { original_region, shadow: shadow.clone(), })); + output.references.bound_symbols.insert(new_symbol); - Pattern::Shadowed(original_region, shadow) + Pattern::Shadowed(original_region, shadow, new_symbol) } }, GlobalTag(name) => { @@ -268,7 +267,7 @@ pub fn canonicalize_pattern<'a>( }, }); } - Err((original_region, shadow)) => { + Err((original_region, shadow, new_symbol)) => { env.problem(Problem::RuntimeError(RuntimeError::Shadowing { original_region, shadow: shadow.clone(), @@ -278,7 +277,8 @@ pub fn canonicalize_pattern<'a>( // are, we're definitely shadowed and will // get a runtime exception as soon as we // encounter the first bad pattern. - opt_erroneous = Some(Pattern::Shadowed(original_region, shadow)); + opt_erroneous = + Some(Pattern::Shadowed(original_region, shadow, new_symbol)); } }; } @@ -339,7 +339,7 @@ pub fn canonicalize_pattern<'a>( }, }); } - Err((original_region, shadow)) => { + Err((original_region, shadow, new_symbol)) => { env.problem(Problem::RuntimeError(RuntimeError::Shadowing { original_region, shadow: shadow.clone(), @@ -349,7 +349,8 @@ pub fn canonicalize_pattern<'a>( // are, we're definitely shadowed and will // get a runtime exception as soon as we // encounter the first bad pattern. - opt_erroneous = Some(Pattern::Shadowed(original_region, shadow)); + opt_erroneous = + Some(Pattern::Shadowed(original_region, shadow, new_symbol)); } }; } @@ -452,7 +453,7 @@ fn add_bindings_from_patterns( use Pattern::*; match pattern { - Identifier(symbol) => { + Identifier(symbol) | Shadowed(_, _, symbol) => { answer.push((*symbol, *region)); } AppliedTag { @@ -477,7 +478,6 @@ fn add_bindings_from_patterns( | FloatLiteral(_, _, _) | StrLiteral(_) | Underscore - | Shadowed(_, _) | MalformedPattern(_, _) | UnsupportedPattern(_) => (), } diff --git a/compiler/can/src/scope.rs b/compiler/can/src/scope.rs index eeab87438d..de7b233b08 100644 --- a/compiler/can/src/scope.rs +++ b/compiler/can/src/scope.rs @@ -110,15 +110,21 @@ impl Scope { exposed_ident_ids: &IdentIds, all_ident_ids: &mut IdentIds, region: Region, - ) -> Result)> { + ) -> Result, Symbol)> { match self.idents.get(&ident) { - Some((_, original_region)) => { + Some(&(_, original_region)) => { let shadow = Loc { - value: ident, + value: ident.clone(), region, }; - Err((*original_region, shadow)) + let ident_id = all_ident_ids.add(ident.clone()); + let symbol = Symbol::new(self.home, ident_id); + + self.symbols.insert(symbol, region); + self.idents.insert(ident, (symbol, region)); + + Err((original_region, shadow, symbol)) } None => { // If this IdentId was already added previously diff --git a/compiler/can/tests/test_can.rs b/compiler/can/tests/test_can.rs index 26ec355746..196be508c0 100644 --- a/compiler/can/tests/test_can.rs +++ b/compiler/can/tests/test_can.rs @@ -368,9 +368,11 @@ mod test_can { let arena = Bump::new(); let CanExprOut { problems, .. } = can_expr_with(&arena, test_home(), src); - assert_eq!(problems.len(), 1); + assert_eq!(problems.len(), 2); assert!(problems.iter().all(|problem| match problem { Problem::RuntimeError(RuntimeError::Shadowing { .. }) => true, + // Due to one of the shadows + Problem::UnusedDef(..) => true, _ => false, })); } @@ -389,9 +391,11 @@ mod test_can { let arena = Bump::new(); let CanExprOut { problems, .. } = can_expr_with(&arena, test_home(), src); - assert_eq!(problems.len(), 1); + assert_eq!(problems.len(), 2); assert!(problems.iter().all(|problem| match problem { Problem::RuntimeError(RuntimeError::Shadowing { .. }) => true, + // Due to one of the shadows + Problem::UnusedDef(..) => true, _ => false, })); } @@ -410,10 +414,12 @@ mod test_can { let arena = Bump::new(); let CanExprOut { problems, .. } = can_expr_with(&arena, test_home(), src); - assert_eq!(problems.len(), 1); + assert_eq!(problems.len(), 2); println!("{:#?}", problems); assert!(problems.iter().all(|problem| match problem { Problem::RuntimeError(RuntimeError::Shadowing { .. }) => true, + // Due to one of the shadows + Problem::UnusedDef(..) => true, _ => false, })); } diff --git a/compiler/constrain/src/pattern.rs b/compiler/constrain/src/pattern.rs index 3844ad3f08..2a1ce6fa46 100644 --- a/compiler/constrain/src/pattern.rs +++ b/compiler/constrain/src/pattern.rs @@ -48,12 +48,11 @@ fn headers_from_annotation_help( headers: &mut SendMap>, ) -> bool { match pattern { - Identifier(symbol) => { + Identifier(symbol) | Shadowed(_, _, symbol) => { headers.insert(*symbol, annotation.clone()); true } Underscore - | Shadowed(_, _) | MalformedPattern(_, _) | UnsupportedPattern(_) | NumLiteral(_, _, _) @@ -159,11 +158,11 @@ pub fn constrain_pattern( PresenceConstraint::IsOpen, )); } - Underscore | UnsupportedPattern(_) | MalformedPattern(_, _) | Shadowed(_, _) => { + Underscore | UnsupportedPattern(_) | MalformedPattern(_, _) => { // Neither the _ pattern nor erroneous ones add any constraints. } - Identifier(symbol) => { + Identifier(symbol) | Shadowed(_, _, symbol) => { if destruct_position { state.constraints.push(Constraint::Present( expected.get_type_ref().clone(), diff --git a/compiler/gen_dev/Cargo.toml b/compiler/gen_dev/Cargo.toml index 41fe784e7e..8efcaca241 100644 --- a/compiler/gen_dev/Cargo.toml +++ b/compiler/gen_dev/Cargo.toml @@ -16,7 +16,7 @@ roc_builtins = { path = "../builtins" } roc_unify = { path = "../unify" } roc_solve = { path = "../solve" } roc_mono = { path = "../mono" } -roc_reporting = { path = "../../reporting" } +roc_error_macros = { path = "../../error_macros" } bumpalo = { version = "3.8.0", features = ["collections"] } target-lexicon = "0.12.2" # TODO: Deal with the update of object to 0.27. diff --git a/compiler/gen_dev/src/generic64/aarch64.rs b/compiler/gen_dev/src/generic64/aarch64.rs index 3c493882dd..591c799052 100644 --- a/compiler/gen_dev/src/generic64/aarch64.rs +++ b/compiler/gen_dev/src/generic64/aarch64.rs @@ -3,9 +3,9 @@ use crate::Relocation; use bumpalo::collections::Vec; use packed_struct::prelude::*; use roc_collections::all::MutMap; +use roc_error_macros::internal_error; use roc_module::symbol::Symbol; use roc_mono::layout::Layout; -use roc_reporting::internal_error; #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] #[allow(dead_code)] diff --git a/compiler/gen_dev/src/generic64/mod.rs b/compiler/gen_dev/src/generic64/mod.rs index 1a3a549be0..26f336ab40 100644 --- a/compiler/gen_dev/src/generic64/mod.rs +++ b/compiler/gen_dev/src/generic64/mod.rs @@ -2,11 +2,11 @@ use crate::{Backend, Env, Relocation}; use bumpalo::collections::Vec; use roc_builtins::bitcode::{FloatWidth, IntWidth}; use roc_collections::all::{MutMap, MutSet}; +use roc_error_macros::internal_error; use roc_module::symbol::{Interns, Symbol}; use roc_mono::code_gen_help::CodeGenHelp; use roc_mono::ir::{BranchInfo, JoinPointId, Literal, Param, ProcLayout, SelfRecursive, Stmt}; use roc_mono::layout::{Builtin, Layout}; -use roc_reporting::internal_error; use std::marker::PhantomData; pub mod aarch64; diff --git a/compiler/gen_dev/src/generic64/x86_64.rs b/compiler/gen_dev/src/generic64/x86_64.rs index 7060f78af0..b6cba72e95 100644 --- a/compiler/gen_dev/src/generic64/x86_64.rs +++ b/compiler/gen_dev/src/generic64/x86_64.rs @@ -5,9 +5,9 @@ use crate::{ use bumpalo::collections::Vec; use roc_builtins::bitcode::{FloatWidth, IntWidth}; use roc_collections::all::MutMap; +use roc_error_macros::internal_error; use roc_module::symbol::Symbol; use roc_mono::layout::{Builtin, Layout}; -use roc_reporting::internal_error; // Not sure exactly how I want to represent registers. // If we want max speed, we would likely make them structs that impl the same trait to avoid ifs. diff --git a/compiler/gen_dev/src/lib.rs b/compiler/gen_dev/src/lib.rs index 20d3d6e8b4..af382ee988 100644 --- a/compiler/gen_dev/src/lib.rs +++ b/compiler/gen_dev/src/lib.rs @@ -5,6 +5,7 @@ use bumpalo::{collections::Vec, Bump}; use roc_builtins::bitcode::{self, FloatWidth, IntWidth}; use roc_collections::all::{MutMap, MutSet}; +use roc_error_macros::internal_error; use roc_module::ident::{ModuleName, TagName}; use roc_module::low_level::{LowLevel, LowLevelWrapperType}; use roc_module::symbol::{Interns, ModuleId, Symbol}; @@ -14,7 +15,6 @@ use roc_mono::ir::{ SelfRecursive, Stmt, }; use roc_mono::layout::{Builtin, Layout, LayoutId, LayoutIds}; -use roc_reporting::internal_error; mod generic64; mod object_builder; diff --git a/compiler/gen_dev/src/object_builder.rs b/compiler/gen_dev/src/object_builder.rs index ac8fa1d922..d8675c880d 100644 --- a/compiler/gen_dev/src/object_builder.rs +++ b/compiler/gen_dev/src/object_builder.rs @@ -8,11 +8,11 @@ use object::{ SymbolFlags, SymbolKind, SymbolScope, }; use roc_collections::all::MutMap; +use roc_error_macros::internal_error; use roc_module::symbol; use roc_module::symbol::Interns; use roc_mono::ir::{Proc, ProcLayout}; use roc_mono::layout::LayoutIds; -use roc_reporting::internal_error; use target_lexicon::{Architecture as TargetArch, BinaryFormat as TargetBF, Triple}; // This is used by some code below which is currently commented out. diff --git a/compiler/gen_llvm/Cargo.toml b/compiler/gen_llvm/Cargo.toml index 2c8fb91ab9..32a79c4767 100644 --- a/compiler/gen_llvm/Cargo.toml +++ b/compiler/gen_llvm/Cargo.toml @@ -10,7 +10,7 @@ edition = "2018" roc_collections = { path = "../collections" } roc_module = { path = "../module" } roc_builtins = { path = "../builtins" } -roc_reporting = { path = "../../reporting" } +roc_error_macros = { path = "../../error_macros" } roc_mono = { path = "../mono" } roc_std = { path = "../../roc_std" } morphic_lib = { path = "../../vendor/morphic_lib" } diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index e30b8aa084..d1a08a4bd8 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -55,6 +55,7 @@ use morphic_lib::{ use roc_builtins::bitcode::{self, FloatWidth, IntWidth, IntrinsicName}; use roc_builtins::{float_intrinsic, int_intrinsic}; use roc_collections::all::{ImMap, MutMap, MutSet}; +use roc_error_macros::internal_error; use roc_module::low_level::LowLevel; use roc_module::symbol::{Interns, ModuleId, Symbol}; use roc_mono::ir::{ @@ -62,7 +63,6 @@ use roc_mono::ir::{ ModifyRc, OptLevel, ProcLayout, }; use roc_mono::layout::{Builtin, LambdaSet, Layout, LayoutIds, TagIdIntType, UnionLayout}; -use roc_reporting::internal_error; use target_lexicon::{Architecture, OperatingSystem, Triple}; /// This is for Inkwell's FunctionValue::verify - we want to know the verification diff --git a/compiler/gen_wasm/Cargo.toml b/compiler/gen_wasm/Cargo.toml index c4e9386c69..90578803f7 100644 --- a/compiler/gen_wasm/Cargo.toml +++ b/compiler/gen_wasm/Cargo.toml @@ -12,4 +12,4 @@ roc_collections = { path = "../collections" } roc_module = { path = "../module" } roc_mono = { path = "../mono" } roc_std = { path = "../../roc_std" } -roc_reporting = { path = "../../reporting" } +roc_error_macros = { path = "../../error_macros" } diff --git a/compiler/gen_wasm/src/backend.rs b/compiler/gen_wasm/src/backend.rs index 97533c074b..1200a80e12 100644 --- a/compiler/gen_wasm/src/backend.rs +++ b/compiler/gen_wasm/src/backend.rs @@ -12,8 +12,8 @@ use roc_mono::ir::{ ProcLayout, Stmt, }; +use roc_error_macros::internal_error; use roc_mono::layout::{Builtin, Layout, LayoutIds, TagIdIntType, UnionLayout}; -use roc_reporting::internal_error; use crate::layout::{CallConv, ReturnMethod, WasmLayout}; use crate::low_level::LowLevelCall; diff --git a/compiler/gen_wasm/src/low_level.rs b/compiler/gen_wasm/src/low_level.rs index 1ea652d999..eae0a0cfb1 100644 --- a/compiler/gen_wasm/src/low_level.rs +++ b/compiler/gen_wasm/src/low_level.rs @@ -1,9 +1,9 @@ use bumpalo::collections::Vec; use roc_builtins::bitcode::{self, FloatWidth, IntWidth}; +use roc_error_macros::internal_error; use roc_module::low_level::{LowLevel, LowLevel::*}; use roc_module::symbol::Symbol; use roc_mono::layout::{Builtin, Layout, UnionLayout}; -use roc_reporting::internal_error; use crate::backend::WasmBackend; use crate::layout::CallConv; diff --git a/compiler/gen_wasm/src/storage.rs b/compiler/gen_wasm/src/storage.rs index 9caa0ba21e..c60220d7ae 100644 --- a/compiler/gen_wasm/src/storage.rs +++ b/compiler/gen_wasm/src/storage.rs @@ -2,9 +2,9 @@ use bumpalo::collections::Vec; use bumpalo::Bump; use roc_collections::all::MutMap; +use roc_error_macros::internal_error; use roc_module::symbol::Symbol; use roc_mono::layout::Layout; -use roc_reporting::internal_error; use crate::layout::{ CallConv, ReturnMethod, StackMemoryFormat, WasmLayout, ZigVersion, BUILTINS_ZIG_VERSION, diff --git a/compiler/gen_wasm/src/wasm_module/code_builder.rs b/compiler/gen_wasm/src/wasm_module/code_builder.rs index ca80469ba7..6ba1042cb1 100644 --- a/compiler/gen_wasm/src/wasm_module/code_builder.rs +++ b/compiler/gen_wasm/src/wasm_module/code_builder.rs @@ -1,7 +1,7 @@ use bumpalo::collections::vec::Vec; use bumpalo::Bump; use core::panic; -use roc_reporting::internal_error; +use roc_error_macros::internal_error; use roc_module::symbol::Symbol; diff --git a/compiler/gen_wasm/src/wasm_module/mod.rs b/compiler/gen_wasm/src/wasm_module/mod.rs index 5f494967b8..2ac492e620 100644 --- a/compiler/gen_wasm/src/wasm_module/mod.rs +++ b/compiler/gen_wasm/src/wasm_module/mod.rs @@ -8,7 +8,7 @@ pub mod serialize; use bumpalo::Bump; pub use code_builder::{Align, CodeBuilder, LocalId, ValueType, VmSymbolState}; pub use linking::SymInfo; -use roc_reporting::internal_error; +use roc_error_macros::internal_error; pub use sections::{ConstExpr, Export, ExportType, Global, GlobalType, Signature}; use crate::wasm_module::serialize::SkipBytes; diff --git a/compiler/gen_wasm/src/wasm_module/opcodes.rs b/compiler/gen_wasm/src/wasm_module/opcodes.rs index cd6f8b59d9..04f779dfbf 100644 --- a/compiler/gen_wasm/src/wasm_module/opcodes.rs +++ b/compiler/gen_wasm/src/wasm_module/opcodes.rs @@ -1,4 +1,4 @@ -use roc_reporting::internal_error; +use roc_error_macros::internal_error; use super::serialize::{parse_u32_or_panic, SkipBytes}; diff --git a/compiler/gen_wasm/src/wasm_module/sections.rs b/compiler/gen_wasm/src/wasm_module/sections.rs index 117d705a07..ea8ca5ec62 100644 --- a/compiler/gen_wasm/src/wasm_module/sections.rs +++ b/compiler/gen_wasm/src/wasm_module/sections.rs @@ -1,7 +1,7 @@ use bumpalo::collections::vec::Vec; use bumpalo::Bump; use roc_collections::all::MutMap; -use roc_reporting::internal_error; +use roc_error_macros::internal_error; use super::dead_code::{ copy_preloads_shrinking_dead_fns, parse_preloads_call_graph, trace_call_graph, diff --git a/compiler/gen_wasm/src/wasm_module/serialize.rs b/compiler/gen_wasm/src/wasm_module/serialize.rs index 8c2204a04b..99561309c5 100644 --- a/compiler/gen_wasm/src/wasm_module/serialize.rs +++ b/compiler/gen_wasm/src/wasm_module/serialize.rs @@ -1,7 +1,7 @@ use std::{fmt::Debug, iter::FromIterator}; use bumpalo::collections::vec::Vec; -use roc_reporting::internal_error; +use roc_error_macros::internal_error; /// In the WebAssembly binary format, all integers are variable-length encoded (using LEB-128) /// A small value like 3 or 100 is encoded as 1 byte. The value 128 needs 2 bytes, etc. diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 9fd2034d0b..9b83b2b388 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -1980,12 +1980,12 @@ fn pattern_to_when<'a>( // for underscore we generate a dummy Symbol (env.unique_symbol(), body) } - Shadowed(region, loc_ident) => { + Shadowed(region, loc_ident, new_symbol) => { let error = roc_problem::can::RuntimeError::Shadowing { original_region: *region, shadow: loc_ident.clone(), }; - (env.unique_symbol(), Loc::at_zero(RuntimeError(error))) + (*new_symbol, Loc::at_zero(RuntimeError(error))) } UnsupportedPattern(region) => { @@ -5118,41 +5118,42 @@ fn register_capturing_closure<'a>( let is_self_recursive = !matches!(recursive, roc_can::expr::Recursive::NotRecursive); - // does this function capture any local values? - let function_layout = layout_cache.raw_from_var(env.arena, function_type, env.subs); - - let captured_symbols = match function_layout { - Ok(RawFunctionLayout::Function(_, lambda_set, _)) => { - if let Layout::Struct(&[]) = lambda_set.runtime_representation() { - CapturedSymbols::None - } else { - let mut temp = Vec::from_iter_in(captured_symbols, env.arena); - temp.sort(); - CapturedSymbols::Captured(temp.into_bump_slice()) + let captured_symbols = match *env.subs.get_content_without_compacting(function_type) { + Content::Structure(FlatType::Func(_, closure_var, _)) => { + match LambdaSet::from_var(env.arena, env.subs, closure_var, env.ptr_bytes) { + Ok(lambda_set) => { + if let Layout::Struct(&[]) = lambda_set.runtime_representation() { + CapturedSymbols::None + } else { + let mut temp = Vec::from_iter_in(captured_symbols, env.arena); + temp.sort(); + CapturedSymbols::Captured(temp.into_bump_slice()) + } + } + Err(_) => { + // just allow this. see https://github.com/rtfeldman/roc/issues/1585 + if captured_symbols.is_empty() { + CapturedSymbols::None + } else { + let mut temp = Vec::from_iter_in(captured_symbols, env.arena); + temp.sort(); + CapturedSymbols::Captured(temp.into_bump_slice()) + } + } } } - Ok(RawFunctionLayout::ZeroArgumentThunk(_)) => { - // top-level thunks cannot capture any variables + _ => { + // This is a value (zero-argument thunk); it cannot capture any variables. debug_assert!( captured_symbols.is_empty(), "{:?} with layout {:?} {:?} {:?}", &captured_symbols, - function_layout, + layout_cache.raw_from_var(env.arena, function_type, env.subs), env.subs, (function_type, closure_type, closure_ext_var), ); CapturedSymbols::None } - Err(_) => { - // just allow this. see https://github.com/rtfeldman/roc/issues/1585 - if captured_symbols.is_empty() { - CapturedSymbols::None - } else { - let mut temp = Vec::from_iter_in(captured_symbols, env.arena); - temp.sort(); - CapturedSymbols::Captured(temp.into_bump_slice()) - } - } }; let partial_proc = PartialProc::from_named_function( @@ -7652,7 +7653,7 @@ fn from_can_pattern_help<'a>( } } StrLiteral(v) => Ok(Pattern::StrLiteral(v.clone())), - Shadowed(region, ident) => Err(RuntimeError::Shadowing { + Shadowed(region, ident, _new_symbol) => Err(RuntimeError::Shadowing { original_region: *region, shadow: ident.clone(), }), diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index f26b8cc3d4..3d19699399 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -7,7 +7,7 @@ use roc_module::ident::{Lowercase, TagName}; use roc_module::symbol::{Interns, Symbol}; use roc_problem::can::RuntimeError; use roc_types::subs::{ - Content, FlatType, RecordFields, Subs, UnionTags, Variable, VariableSubsSlice, + Content, FlatType, RecordFields, Subs, UnionTags, UnsortedUnionTags, Variable, }; use roc_types::types::{gather_fields_unsorted_iter, RecordField}; use std::collections::hash_map::Entry; @@ -1580,18 +1580,26 @@ fn layout_from_flat_type<'a>( } } TagUnion(tags, ext_var) => { + let (tags, ext_var) = tags.unsorted_tags_and_ext(subs, ext_var); + debug_assert!(ext_var_is_empty_tag_union(subs, ext_var)); - Ok(layout_from_tag_union(arena, tags, subs, env.ptr_bytes)) + Ok(layout_from_tag_union(arena, &tags, subs, env.ptr_bytes)) } FunctionOrTagUnion(tag_name, _, ext_var) => { - debug_assert!(ext_var_is_empty_tag_union(subs, ext_var)); + debug_assert!( + ext_var_is_empty_tag_union(subs, ext_var), + "If ext_var wasn't empty, this wouldn't be a FunctionOrTagUnion!" + ); - let tags = UnionTags::from_tag_name_index(tag_name); + let union_tags = UnionTags::from_tag_name_index(tag_name); + let (tags, _) = union_tags.unsorted_tags_and_ext(subs, ext_var); - Ok(layout_from_tag_union(arena, tags, subs, env.ptr_bytes)) + Ok(layout_from_tag_union(arena, &tags, subs, env.ptr_bytes)) } RecursiveTagUnion(rec_var, tags, ext_var) => { + let (tags, ext_var) = tags.unsorted_tags_and_ext(subs, ext_var); + debug_assert!(ext_var_is_empty_tag_union(subs, ext_var)); // some observations @@ -1603,9 +1611,8 @@ fn layout_from_flat_type<'a>( // That means none of the optimizations for enums or single tag tag unions apply let rec_var = subs.get_root_key_without_compacting(rec_var); - let mut tag_layouts = Vec::with_capacity_in(tags.len(), arena); - - let tags_vec = cheap_sort_tags(arena, tags, subs); + let tags_vec = tags.tags; + let mut tag_layouts = Vec::with_capacity_in(tags_vec.len(), arena); let mut nullable = None; @@ -1619,7 +1626,7 @@ fn layout_from_flat_type<'a>( } env.insert_seen(rec_var); - for (index, (_name, variables)) in tags_vec.into_iter().enumerate() { + for (index, &(_name, variables)) in tags_vec.iter().enumerate() { if matches!(nullable, Some(i) if i == index as TagIdIntType) { // don't add the nullable case continue; @@ -1627,8 +1634,7 @@ fn layout_from_flat_type<'a>( let mut tag_layout = Vec::with_capacity_in(variables.len() + 1, arena); - for var_index in variables { - let var = subs[var_index]; + for &var in variables { // TODO does this cause problems with mutually recursive unions? if rec_var == subs.get_root_key_without_compacting(var) { tag_layout.push(Layout::RecursivePointer); @@ -1911,13 +1917,14 @@ fn is_recursive_tag_union(layout: &Layout) -> bool { fn union_sorted_tags_help_new<'a>( arena: &'a Bump, - mut tags_vec: Vec<(&'_ TagName, VariableSubsSlice)>, + tags_list: &[(&'_ TagName, &[Variable])], opt_rec_var: Option, subs: &Subs, ptr_bytes: u32, ) -> UnionVariant<'a> { // sort up front; make sure the ordering stays intact! - tags_vec.sort_unstable_by(|(a, _), (b, _)| a.cmp(b)); + let mut tags_list = Vec::from_iter_in(tags_list.iter(), arena); + tags_list.sort_unstable_by(|(a, _), (b, _)| a.cmp(b)); let mut env = Env { arena, @@ -1926,27 +1933,26 @@ fn union_sorted_tags_help_new<'a>( ptr_bytes, }; - match tags_vec.len() { + match tags_list.len() { 0 => { // trying to instantiate a type with no values UnionVariant::Never } 1 => { - let (tag_name, arguments) = tags_vec.remove(0); + let &(tag_name, arguments) = tags_list.remove(0); let tag_name = tag_name.clone(); // just one tag in the union (but with arguments) can be a struct - let mut layouts = Vec::with_capacity_in(tags_vec.len(), arena); + let mut layouts = Vec::with_capacity_in(tags_list.len(), arena); // special-case NUM_AT_NUM: if its argument is a FlexVar, make it Int match tag_name { TagName::Private(Symbol::NUM_AT_NUM) => { - let var = subs[arguments.into_iter().next().unwrap()]; + let var = arguments[0]; layouts.push(unwrap_num_tag(subs, var, ptr_bytes).expect("invalid num layout")); } _ => { - for var_index in arguments { - let var = subs[var_index]; + for &var in arguments { match Layout::from_var(&mut env, var) { Ok(layout) => { layouts.push(layout); @@ -1989,7 +1995,7 @@ fn union_sorted_tags_help_new<'a>( } num_tags => { // default path - let mut answer = Vec::with_capacity_in(tags_vec.len(), arena); + let mut answer = Vec::with_capacity_in(tags_list.len(), arena); let mut has_any_arguments = false; let mut nullable: Option<(TagIdIntType, TagName)> = None; @@ -1997,7 +2003,7 @@ fn union_sorted_tags_help_new<'a>( // only recursive tag unions can be nullable let is_recursive = opt_rec_var.is_some(); if is_recursive && GENERATE_NULLABLE { - for (index, (name, variables)) in tags_vec.iter().enumerate() { + for (index, (name, variables)) in tags_list.iter().enumerate() { if variables.is_empty() { nullable = Some((index as TagIdIntType, (*name).clone())); break; @@ -2005,7 +2011,7 @@ fn union_sorted_tags_help_new<'a>( } } - for (index, (tag_name, arguments)) in tags_vec.into_iter().enumerate() { + for (index, &(tag_name, arguments)) in tags_list.into_iter().enumerate() { // reserve space for the tag discriminant if matches!(nullable, Some((i, _)) if i as usize == index) { debug_assert!(arguments.is_empty()); @@ -2014,8 +2020,7 @@ fn union_sorted_tags_help_new<'a>( let mut arg_layouts = Vec::with_capacity_in(arguments.len() + 1, arena); - for var_index in arguments { - let var = subs[var_index]; + for &var in arguments { match Layout::from_var(&mut env, var) { Ok(layout) => { has_any_arguments = true; @@ -2326,38 +2331,15 @@ pub fn union_sorted_tags_help<'a>( } } -fn cheap_sort_tags<'a, 'b>( - arena: &'a Bump, - tags: UnionTags, - subs: &'b Subs, -) -> Vec<'a, (&'b TagName, VariableSubsSlice)> { - let mut tags_vec = Vec::with_capacity_in(tags.len(), arena); - - for (tag_index, index) in tags.iter_all() { - let tag = &subs[tag_index]; - let slice = subs[index]; - - tags_vec.push((tag, slice)); - } - - tags_vec -} - fn layout_from_newtype<'a>( arena: &'a Bump, - tags: UnionTags, + tags: &UnsortedUnionTags, subs: &Subs, ptr_bytes: u32, ) -> Layout<'a> { debug_assert!(tags.is_newtype_wrapper(subs)); - let slice_index = tags.variables().into_iter().next().unwrap(); - let slice = subs[slice_index]; - let var_index = slice.into_iter().next().unwrap(); - let var = subs[var_index]; - - let tag_name_index = tags.tag_names().into_iter().next().unwrap(); - let tag_name = &subs[tag_name_index]; + let (tag_name, var) = tags.get_newtype(subs); if tag_name == &TagName::Private(Symbol::NUM_AT_NUM) { unwrap_num_tag(subs, var, ptr_bytes).expect("invalid Num argument") @@ -2388,7 +2370,7 @@ fn layout_from_newtype<'a>( fn layout_from_tag_union<'a>( arena: &'a Bump, - tags: UnionTags, + tags: &UnsortedUnionTags, subs: &Subs, ptr_bytes: u32, ) -> Layout<'a> { @@ -2398,14 +2380,13 @@ fn layout_from_tag_union<'a>( return layout_from_newtype(arena, tags, subs, ptr_bytes); } - let tags_vec = cheap_sort_tags(arena, tags, subs); + let tags_vec = &tags.tags; match tags_vec.get(0) { Some((tag_name, arguments)) if *tag_name == &TagName::Private(Symbol::NUM_AT_NUM) => { debug_assert_eq!(arguments.len(), 1); - let var_index = arguments.into_iter().next().unwrap(); - let var = subs[var_index]; + let &var = arguments.iter().next().unwrap(); unwrap_num_tag(subs, var, ptr_bytes).expect("invalid Num argument") } diff --git a/compiler/test_gen/src/gen_tags.rs b/compiler/test_gen/src/gen_tags.rs index b8062dbde2..bbbc9fa490 100644 --- a/compiler/test_gen/src/gen_tags.rs +++ b/compiler/test_gen/src/gen_tags.rs @@ -1367,3 +1367,85 @@ fn monomorphized_tag_with_polymorphic_arg_and_monomorphic_arg() { u8 ) } + +#[test] +#[cfg(any(feature = "gen-llvm"))] +fn issue_2365_monomorphize_tag_with_non_empty_ext_var() { + assert_evals_to!( + indoc!( + r#" + app "test" provides [main] to "./platform" + + Single a : [A, B, C]a + Compound a : Single [D, E, F]a + + single : {} -> Single * + single = \{} -> C + + compound : {} -> Compound * + compound = \{} -> single {} + + main = compound {} + "# + ), + 2, // C + u8 + ) +} + +#[test] +#[cfg(any(feature = "gen-llvm"))] +fn issue_2365_monomorphize_tag_with_non_empty_ext_var_wrapped() { + assert_evals_to!( + indoc!( + r#" + app "test" provides [main] to "./platform" + + Single a : [A, B, C]a + Compound a : Single [D, E, F]a + + single : {} -> Result Str (Single *) + single = \{} -> Err C + + compound : {} -> Result Str (Compound *) + compound = \{} -> + when single {} is + Ok s -> Ok s + Err e -> Err e + + main = compound {} + "# + ), + 2, // C + u8 + ) +} + +#[test] +#[cfg(any(feature = "gen-llvm"))] +fn issue_2365_monomorphize_tag_with_non_empty_ext_var_wrapped_nested() { + assert_evals_to!( + indoc!( + r#" + app "test" provides [main] to "./platform" + + Single a : [A, B, C]a + Compound a : Single [D, E, F]a + + main = + single : {} -> Result Str (Single *) + single = \{} -> Err C + + compound : {} -> Result Str (Compound *) + compound = \{} -> + when single {} is + Ok s -> Ok s + Err e -> Err e + + compound {} + "# + ), + 2, // C + u8 + ) +} diff --git a/compiler/types/src/pretty_print.rs b/compiler/types/src/pretty_print.rs index 6a48f7088f..a861242bd5 100644 --- a/compiler/types/src/pretty_print.rs +++ b/compiler/types/src/pretty_print.rs @@ -406,8 +406,8 @@ fn write_sorted_tags2<'a>( ext_var: Variable, ) -> ExtContent<'a> { // Sort the fields so they always end up in the same order. - let (it, new_ext_var) = tags.unsorted_iterator_and_ext(subs, ext_var); - let mut sorted_fields: Vec<_> = it.collect(); + let (tags, new_ext_var) = tags.unsorted_tags_and_ext(subs, ext_var); + let mut sorted_fields = tags.tags; let interns = &env.interns; let home = env.home; diff --git a/compiler/types/src/subs.rs b/compiler/types/src/subs.rs index 08bd430f36..2f27493b8e 100644 --- a/compiler/types/src/subs.rs +++ b/compiler/types/src/subs.rs @@ -1813,16 +1813,17 @@ impl UnionTags { it.map(f) } - pub fn unsorted_iterator_and_ext<'a>( + #[inline(always)] + pub fn unsorted_tags_and_ext<'a>( &'a self, subs: &'a Subs, ext: Variable, - ) -> (impl Iterator + 'a, Variable) { + ) -> (UnsortedUnionTags<'a>, Variable) { let (it, ext) = crate::types::gather_tags_unsorted_iter(subs, *self, ext); - let f = move |(label, slice): (_, SubsSlice)| (label, subs.get_subs_slice(slice)); + let it = it.map(f); - (it.map(f), ext) + (UnsortedUnionTags { tags: it.collect() }, ext) } #[inline(always)] @@ -1877,6 +1878,25 @@ impl UnionTags { } } +#[derive(Debug)] +pub struct UnsortedUnionTags<'a> { + pub tags: Vec<(&'a TagName, &'a [Variable])>, +} + +impl<'a> UnsortedUnionTags<'a> { + pub fn is_newtype_wrapper(&self, _subs: &Subs) -> bool { + if self.tags.len() != 1 { + return false; + } + self.tags[0].1.len() == 1 + } + + pub fn get_newtype(&self, _subs: &Subs) -> (&TagName, Variable) { + let (tag_name, vars) = self.tags[0]; + (tag_name, vars[0]) + } +} + pub type SortedTagsIterator<'a> = Box + 'a>; pub type SortedTagsSlicesIterator<'a> = Box + 'a>; @@ -2025,6 +2045,21 @@ impl RecordFields { it } + #[inline(always)] + pub fn unsorted_iterator_and_ext<'a>( + &'a self, + subs: &'a Subs, + ext: Variable, + ) -> ( + impl Iterator)> + 'a, + Variable, + ) { + let (it, ext) = crate::types::gather_fields_unsorted_iter(subs, *self, ext) + .expect("Something weird ended up in a record type"); + + (it, ext) + } + /// Get a sorted iterator over the fields of this record type /// /// Implementation: When the record has an `ext` variable that is the empty record, then diff --git a/error_macros/Cargo.toml b/error_macros/Cargo.toml new file mode 100644 index 0000000000..d5c7276898 --- /dev/null +++ b/error_macros/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "roc_error_macros" +version = "0.1.0" +authors = ["The Roc Contributors"] +license = "UPL-1.0" +edition = "2018" + +[dependencies] diff --git a/error_macros/src/lib.rs b/error_macros/src/lib.rs new file mode 100644 index 0000000000..d9b2e0e9a3 --- /dev/null +++ b/error_macros/src/lib.rs @@ -0,0 +1,31 @@ +/// `internal_error!` should be used whenever a compiler invariant is broken. +/// It is a wrapper around panic that tells the user to file a bug. +/// This should only be used in cases where there would be a compiler bug and the user can't fix it. +/// If there is simply an unimplemented feature, please use `unimplemented!` +/// If there is a user error, please use roc_reporting to print a nice error message. +#[macro_export] +macro_rules! internal_error { + ($($arg:tt)*) => ({ + eprintln!("An internal compiler expectation was broken."); + eprintln!("This is definitely a compiler bug."); + // TODO: update this to the new bug template. + eprintln!("Please file an issue here: https://github.com/rtfeldman/roc/issues/new/choose"); + #[allow(clippy::panic)] { + panic!($($arg)*); + } + }) +} + +/// `user_error!` should only ever be used temporarily. +/// It is a way to document locations where we do not yet have nice error reporting. +/// All cases of `user_error!` should eventually be replaced with pretty error printing using roc_reporting. +#[macro_export] +macro_rules! user_error { + ($($arg:tt)*) => ({ + eprintln!("We ran into an issue while compiling your code."); + eprintln!("Sadly, we don't havs a pretty error message for this case yet."); + eprintln!("If you can't figure out the problem from the context below, please reach out at: https://roc.zulipchat.com/"); + eprintln!($($arg)*); + std::process::exit(1); + }) +} diff --git a/examples/benchmarks/Deriv.roc b/examples/benchmarks/Deriv.roc index 2cad781765..5e5a81f8b2 100644 --- a/examples/benchmarks/Deriv.roc +++ b/examples/benchmarks/Deriv.roc @@ -21,22 +21,6 @@ main = |> Task.map (\_ -> {}) -nest : (I64, Expr -> IO Expr), I64, Expr -> IO Expr -nest = \f, n, e -> Task.loop { s: n, f, m: n, x: e } nestHelp - -State : { s : I64, f : I64, Expr -> IO Expr, m : I64, x : Expr } - -nestHelp : State -> IO [ Step State, Done Expr ] -nestHelp = \{ s, f, m, x } -> - when m is - 0 -> - Task.succeed (Done x) - - _ -> - w <- Task.after (f (s - m) x) - - Task.succeed (Step { s, f, m: (m - 1), x: w }) - Expr : [ Val I64, Var Str, Add Expr Expr, Mul Expr Expr, Pow Expr Expr, Ln Expr ] divmod : I64, I64 -> Result { div : I64, mod : I64 } [ DivByZero ]* @@ -196,6 +180,18 @@ count = \expr -> Ln f -> count f +nest : (I64, Expr -> IO Expr), I64, Expr -> IO Expr +nest = \f, n, e -> nestHelp n f n e + +nestHelp : I64, (I64, Expr -> IO Expr), I64, Expr -> IO Expr +nestHelp = \s, f, m, x -> + when m is + 0 -> + Task.succeed x + + _ -> + f (s - m) x |> Task.after \w -> nestHelp s f (m - 1) w + deriv : I64, Expr -> IO Expr deriv = \i, f -> fprime = d "x" f diff --git a/examples/false-interpreter/False.roc b/examples/false-interpreter/False.roc index d7e0ac0c4f..57fc3c14a9 100644 --- a/examples/false-interpreter/False.roc +++ b/examples/false-interpreter/False.roc @@ -89,10 +89,6 @@ isWhitespace = \char -> == 0x9# tab interpretCtx : Context -> Task Context InterpreterErrors interpretCtx = \ctx -> - Task.loop ctx interpretCtxLoop - -interpretCtxLoop : Context -> Task [ Step Context, Done Context ] InterpreterErrors -interpretCtxLoop = \ctx -> when ctx.state is Executing if Context.inWhileScope ctx -> # Deal with the current while loop potentially looping. @@ -108,11 +104,11 @@ interpretCtxLoop = \ctx -> if n == 0 then newScope = { scope & whileInfo: None } - Task.succeed (Step { popCtx & scopes: List.set ctx.scopes last newScope }) + interpretCtx { popCtx & scopes: List.set ctx.scopes last newScope } else newScope = { scope & whileInfo: Some { state: InBody, body, cond } } - Task.succeed (Step { popCtx & scopes: List.append (List.set ctx.scopes last newScope) { data: None, buf: body, index: 0, whileInfo: None } }) + interpretCtx { popCtx & scopes: List.append (List.set ctx.scopes last newScope) { data: None, buf: body, index: 0, whileInfo: None } } Err e -> Task.fail e @@ -121,7 +117,7 @@ interpretCtxLoop = \ctx -> # Just rand the body. Run the condition again. newScope = { scope & whileInfo: Some { state: InCond, body, cond } } - Task.succeed (Step { ctx & scopes: List.append (List.set ctx.scopes last newScope) { data: None, buf: cond, index: 0, whileInfo: None } }) + interpretCtx { ctx & scopes: List.append (List.set ctx.scopes last newScope) { data: None, buf: cond, index: 0, whileInfo: None } } None -> Task.fail NoScope @@ -135,7 +131,7 @@ interpretCtxLoop = \ctx -> when result is Ok (T val newCtx) -> execCtx <- Task.await (stepExecCtx newCtx val) - Task.succeed (Step execCtx) + interpretCtx execCtx Err NoScope -> Task.fail NoScope @@ -147,9 +143,9 @@ interpretCtxLoop = \ctx -> # If no scopes left, all execution complete. if List.isEmpty dropCtx.scopes then - Task.succeed (Done dropCtx) + Task.succeed dropCtx else - Task.succeed (Step dropCtx) + interpretCtx dropCtx InComment -> result <- Task.attempt (Context.getChar ctx) @@ -157,9 +153,9 @@ interpretCtxLoop = \ctx -> Ok (T val newCtx) -> if val == 0x7D then # `}` end of comment - Task.succeed (Step { newCtx & state: Executing }) + interpretCtx { newCtx & state: Executing } else - Task.succeed (Step { newCtx & state: InComment }) + interpretCtx { newCtx & state: InComment } Err NoScope -> Task.fail NoScope @@ -178,13 +174,13 @@ interpretCtxLoop = \ctx -> # so this is make i64 mul by 10 then convert back to i32. nextAccum = (10 * Num.intCast accum) + Num.intCast (val - 0x30) - Task.succeed (Step { newCtx & state: InNumber (Num.intCast nextAccum) }) + interpretCtx { newCtx & state: InNumber (Num.intCast nextAccum) } else # outside of number now, this needs to be executed. pushCtx = Context.pushStack newCtx (Number accum) execCtx <- Task.await (stepExecCtx { pushCtx & state: Executing } val) - Task.succeed (Step execCtx) + interpretCtx execCtx Err NoScope -> Task.fail NoScope @@ -201,12 +197,12 @@ interpretCtxLoop = \ctx -> when Str.fromUtf8 bytes is Ok str -> { } <- Task.await (Stdout.raw str) - Task.succeed (Step { newCtx & state: Executing }) + interpretCtx { newCtx & state: Executing } Err _ -> Task.fail BadUtf8 else - Task.succeed (Step { newCtx & state: InString (List.append bytes val) }) + interpretCtx { newCtx & state: InString (List.append bytes val) } Err NoScope -> Task.fail NoScope @@ -220,17 +216,17 @@ interpretCtxLoop = \ctx -> Ok (T val newCtx) -> if val == 0x5B then # start of a nested lambda `[` - Task.succeed (Step { newCtx & state: InLambda (depth + 1) (List.append bytes val) }) + interpretCtx { newCtx & state: InLambda (depth + 1) (List.append bytes val) } else if val == 0x5D then # `]` end of current lambda if depth == 0 then # end of all lambdas - Task.succeed (Step (Context.pushStack { newCtx & state: Executing } (Lambda bytes))) + interpretCtx (Context.pushStack { newCtx & state: Executing } (Lambda bytes)) else # end of nested lambda - Task.succeed (Step { newCtx & state: InLambda (depth - 1) (List.append bytes val) }) + interpretCtx { newCtx & state: InLambda (depth - 1) (List.append bytes val) } else - Task.succeed (Step { newCtx & state: InLambda depth (List.append bytes val) }) + interpretCtx { newCtx & state: InLambda depth (List.append bytes val) } Err NoScope -> Task.fail NoScope @@ -256,14 +252,14 @@ interpretCtxLoop = \ctx -> when result2 is Ok a -> - Task.succeed (Step a) + interpretCtx a Err e -> Task.fail e Ok (T 0x9F newCtx) -> # This is supposed to flush io buffers. We don't buffer, so it does nothing - Task.succeed (Step newCtx) + interpretCtx newCtx Ok (T x _) -> data = Num.toStr (Num.intCast x) @@ -280,7 +276,7 @@ interpretCtxLoop = \ctx -> result <- Task.attempt (Context.getChar { ctx & state: Executing }) when result is Ok (T x newCtx) -> - Task.succeed (Step (Context.pushStack newCtx (Number (Num.intCast x)))) + interpretCtx (Context.pushStack newCtx (Number (Num.intCast x))) Err NoScope -> Task.fail NoScope diff --git a/examples/false-interpreter/platform/Task.roc b/examples/false-interpreter/platform/Task.roc index 0ad7c223b2..520eba4976 100644 --- a/examples/false-interpreter/platform/Task.roc +++ b/examples/false-interpreter/platform/Task.roc @@ -1,27 +1,9 @@ interface Task - exposes [ Task, succeed, fail, await, map, onFail, attempt, fromResult, loop ] + exposes [ Task, succeed, fail, await, map, onFail, attempt, fromResult ] imports [ fx.Effect ] Task ok err : Effect.Effect (Result ok err) -loop : state, (state -> Task [ Step state, Done done ] err) -> Task done err -loop = \state, step -> - looper = \current -> - step current - |> Effect.map - \res -> - when res is - Ok (Step newState) -> - Step newState - - Ok (Done result) -> - Done (Ok result) - - Err e -> - Done (Err e) - - Effect.loop state looper - succeed : val -> Task val * succeed = \val -> Effect.always (Ok val) diff --git a/reporting/src/lib.rs b/reporting/src/lib.rs index 1f7520b931..ff2791a5f8 100644 --- a/reporting/src/lib.rs +++ b/reporting/src/lib.rs @@ -4,35 +4,3 @@ pub mod error; pub mod report; - -/// `internal_error!` should be used whenever a compiler invariant is broken. -/// It is a wrapper around panic that tells the user to file a bug. -/// This should only be used in cases where there would be a compiler bug and the user can't fix it. -/// If there is simply an unimplemented feature, please use `unimplemented!` -/// If there is a user error, please use roc_reporting to print a nice error message. -#[macro_export] -macro_rules! internal_error { - ($($arg:tt)*) => ({ - eprintln!("An internal compiler expectation was broken."); - eprintln!("This is definitely a compiler bug."); - // TODO: update this to the new bug template. - eprintln!("Please file an issue here: https://github.com/rtfeldman/roc/issues/new/choose"); - #[allow(clippy::panic)] { - panic!($($arg)*); - } - }) -} - -/// `user_error!` should only ever be used temporarily. -/// It is a way to document locations where we do not yet have nice error reporting. -/// All cases of `user_error!` should eventually be replaced with pretty error printing using roc_reporting. -#[macro_export] -macro_rules! user_error { - ($($arg:tt)*) => ({ - eprintln!("We ran into an issue while compiling your code."); - eprintln!("Sadly, we don't havs a pretty error message for this case yet."); - eprintln!("If you can't figure out the problem from the context below, please reach out at: https://roc.zulipchat.com/"); - eprintln!($($arg)*); - std::process::exit(1); - }) -} diff --git a/reporting/tests/test_reporting.rs b/reporting/tests/test_reporting.rs index d69da25ae1..c9550b9ab0 100644 --- a/reporting/tests/test_reporting.rs +++ b/reporting/tests/test_reporting.rs @@ -424,6 +424,16 @@ mod test_reporting { `Booly` is not used anywhere in your code. + 3│ Booly : [ Yes, No, Maybe ] + ^^^^^^^^^^^^^^^^^^^^^^^^^^ + + If you didn't intend on using `Booly` then remove it so future readers + of your code don't wonder why it is there. + + ── UNUSED DEFINITION ─────────────────────────────────────────────────────────── + + `Booly` is not used anywhere in your code. + 1│ Booly : [ Yes, No ] ^^^^^^^^^^^^^^^^^^^