diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 8f7197b641..4b690b61aa 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,7 +1,6 @@ on: workflow_dispatch: -# pull_request: -# TODO remove pull_request trigger + #pull_request: name: Docker images tests @@ -19,8 +18,10 @@ jobs: - name: Build image run: docker compose -f docker/nightly-ubuntu-latest/docker-compose.yml build - - name: Run hello world test - run: docker compose -f docker/nightly-ubuntu-latest/docker-compose.yml run roc examples/helloWorld.roc + - name: Test with hello world + run: | + curl -OL https://raw.githubusercontent.com/roc-lang/examples/refs/heads/main/examples/HelloWorld/main.roc + docker compose -f docker/nightly-ubuntu-latest/docker-compose.yml run roc main.roc nightly-ubuntu-2204: @@ -36,8 +37,10 @@ jobs: - name: Build image run: docker compose -f docker/nightly-ubuntu-2204/docker-compose.yml build - - name: Run hello world test - run: docker compose -f docker/nightly-ubuntu-2204/docker-compose.yml run roc examples/helloWorld.roc + - name: Test with hello world + run: | + curl -OL https://raw.githubusercontent.com/roc-lang/examples/refs/heads/main/examples/HelloWorld/main.roc + docker compose -f docker/nightly-ubuntu-2204/docker-compose.yml run roc main.roc nightly-ubuntu-2004: name: nightly-ubuntu-2004 @@ -52,8 +55,10 @@ jobs: - name: Build image run: docker compose -f docker/nightly-ubuntu-2004/docker-compose.yml build - - name: Run hello world test - run: docker compose -f docker/nightly-ubuntu-2004/docker-compose.yml run roc examples/helloWorld.roc + - name: Test with hello world + run: | + curl -OL https://raw.githubusercontent.com/roc-lang/examples/refs/heads/main/examples/HelloWorld/main.roc + docker compose -f docker/nightly-ubuntu-2004/docker-compose.yml run roc main.roc nightly-debian-latest: name: nightly-debian-latest @@ -68,8 +73,10 @@ jobs: - name: Build image run: docker compose -f docker/nightly-debian-latest/docker-compose.yml build - - name: Run hello world test - run: docker compose -f docker/nightly-debian-latest/docker-compose.yml run roc examples/helloWorld.roc + - name: Test with hello world + run: | + curl -OL https://raw.githubusercontent.com/roc-lang/examples/refs/heads/main/examples/HelloWorld/main.roc + docker compose -f docker/nightly-debian-latest/docker-compose.yml run roc main.roc nightly-debian-bookworm: name: nightly-debian-bookworm @@ -84,8 +91,10 @@ jobs: - name: Build image run: docker compose -f docker/nightly-debian-bookworm/docker-compose.yml build - - name: Run hello world test - run: docker compose -f docker/nightly-debian-bookworm/docker-compose.yml run roc examples/helloWorld.roc + - name: Test with hello world + run: | + curl -OL https://raw.githubusercontent.com/roc-lang/examples/refs/heads/main/examples/HelloWorld/main.roc + docker compose -f docker/nightly-debian-bookworm/docker-compose.yml run roc main.roc nightly-debian-buster: name: nightly-debian-buster @@ -100,5 +109,7 @@ jobs: - name: Build image run: docker compose -f docker/nightly-debian-buster/docker-compose.yml build - - name: Run hello world test - run: docker compose -f docker/nightly-debian-buster/docker-compose.yml run roc examples/helloWorld.roc + - name: Test with hello world + run: | + curl -OL https://raw.githubusercontent.com/roc-lang/examples/refs/heads/main/examples/HelloWorld/main.roc + docker compose -f docker/nightly-debian-buster/docker-compose.yml run roc main.roc diff --git a/.github/workflows/nightly_macos_apple_silicon.yml b/.github/workflows/nightly_macos_apple_silicon.yml index 500a10459f..eec4589677 100644 --- a/.github/workflows/nightly_macos_apple_silicon.yml +++ b/.github/workflows/nightly_macos_apple_silicon.yml @@ -58,7 +58,7 @@ jobs: - name: test with zig platform run: | - cd ${{ env.RELEASE_FOLDER_NAME }} && ./roc --build-host --suppress-build-host-warning crates/cli/tests/test-projects/test-platform-simple-zig/app.roc + cd ${{ env.RELEASE_FOLDER_NAME }} && ./roc --build-host --suppress-build-host-warning examples/platform-switching/rocLovesZig.roc - name: print short commit SHA run: git rev-parse --short "$GITHUB_SHA" diff --git a/.github/workflows/test_nightly_macos_apple_silicon.yml b/.github/workflows/test_nightly_macos_apple_silicon.yml index f642716895..61ee6ba7c9 100644 --- a/.github/workflows/test_nightly_macos_apple_silicon.yml +++ b/.github/workflows/test_nightly_macos_apple_silicon.yml @@ -26,14 +26,11 @@ jobs: - name: rename nightly folder run: mv roc_nightly* roc_nightly - - name: test roc hello world - run: cd roc_nightly && ./roc examples/helloWorld.roc - - name: test platform switching zig - run: cd roc_nightly && ./roc examples/platform-switching/rocLovesZig.roc + run: cd roc_nightly && ./roc examples/platform-switching/rocLovesZig.roc --build-host --suppress-build-host-warning - name: test platform switching c - run: cd roc_nightly && ./roc examples/platform-switching/rocLovesC.roc + run: cd roc_nightly && ./roc examples/platform-switching/rocLovesC.roc --build-host --suppress-build-host-warning - name: test repl run: | diff --git a/ci/basic_nightly_test.sh b/ci/basic_nightly_test.sh index 66640cdd2b..d4ad10915a 100755 --- a/ci/basic_nightly_test.sh +++ b/ci/basic_nightly_test.sh @@ -28,9 +28,6 @@ mv roc_nightly* roc_nightly cd roc_nightly -# test roc hello world -./roc examples/helloWorld.roc - # test rust platform (first prebuild the host) examples/platform-switching/rust-platform/build.sh ./roc examples/platform-switching/rocLovesRust.roc diff --git a/ci/build_basic_cli.sh b/ci/build_basic_cli.sh index 9699df5fbe..7726917421 100755 --- a/ci/build_basic_cli.sh +++ b/ci/build_basic_cli.sh @@ -54,6 +54,6 @@ fi ./jump-start.sh # build the basic cli platform -roc build.roc --prebuilt-platform +roc build.roc cd .. diff --git a/ci/build_basic_webserver.sh b/ci/build_basic_webserver.sh index 84e4b434d9..81a886a248 100755 --- a/ci/build_basic_webserver.sh +++ b/ci/build_basic_webserver.sh @@ -47,6 +47,6 @@ cd .. cd basic-webserver -roc build.roc --prebuilt-platform +roc build.roc cd .. diff --git a/ci/package_release.sh b/ci/package_release.sh index f12b523f9a..40813b1918 100755 --- a/ci/package_release.sh +++ b/ci/package_release.sh @@ -12,7 +12,7 @@ mkdir -p $1 $1/examples $1/crates/compiler/builtins/bitcode mv target/release-with-lto/{roc,roc_language_server,lib} $1 mv LICENSE LEGAL_DETAILS $1 -mv examples/{helloWorld.roc,platform-switching,cli} $1/examples +mv examples/{platform-switching,cli} $1/examples mv crates/roc_std $1/crates mv crates/compiler/builtins/bitcode/src $1/crates/compiler/builtins/bitcode diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index cba3bc7d63..a31e053b98 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -274,6 +274,7 @@ pub fn build_app() -> Command { Arg::new(ROC_FILE) .help("The .roc file to test") .value_parser(value_parser!(PathBuf)) + .num_args(0..) .required(false) .default_value(DEFAULT_ROC_FILENAME) ) @@ -516,18 +517,21 @@ pub fn test(matches: &ArgMatches, target: Target) -> io::Result { Some(n) => Threading::AtMost(*n), }; - let path = matches.get_one::(ROC_FILE).unwrap(); + let paths: Vec<_> = matches.get_many::(ROC_FILE).unwrap().collect(); - // Spawn the root task - if !path.exists() { - let current_dir = env::current_dir().unwrap(); - let expected_file_path = current_dir.join(path); + let paths: Vec<_> = { + let mut flatten_paths: Vec<_> = vec![]; + for path in paths.into_iter() { + // Spawn the root task + if !path.exists() { + let current_dir = env::current_dir().unwrap(); + let expected_file_path = current_dir.join(path); - let current_dir_string = current_dir.display(); - let expected_file_path_string = expected_file_path.display(); + let current_dir_string = current_dir.display(); + let expected_file_path_string = expected_file_path.display(); - // TODO these should use roc_reporting to display nicer error messages. - match matches.value_source(ROC_FILE) { + // TODO these should use roc_reporting to display nicer error messages. + match matches.value_source(ROC_FILE) { Some(ValueSource::DefaultValue) => { eprintln!( "\nThe current directory ({current_dir_string}) does not contain a {DEFAULT_ROC_FILENAME} file to use as a default.\n\nYou can run `roc help` for more information on how to provide a .roc file.\n" @@ -535,116 +539,141 @@ pub fn test(matches: &ArgMatches, target: Target) -> io::Result { } _ => eprintln!("\nThis file was not found: {expected_file_path_string}\n\nYou can run `roc help` for more information on how to provide a .roc file.\n"), } - - process::exit(1); - } - - let arena = &arena; - let function_kind = FunctionKind::from_env(); - - let opt_main_path = matches.get_one::(FLAG_MAIN); - - // Step 1: compile the app and generate the .o file - let load_config = LoadConfig { - target, - function_kind, - // TODO: expose this from CLI? - render: roc_reporting::report::RenderTarget::ColorTerminal, - palette: roc_reporting::report::DEFAULT_PALETTE, - threading, - exec_mode: ExecutionMode::Test, - }; - let load_result = roc_load::load_and_monomorphize( - arena, - path.to_path_buf(), - opt_main_path.cloned(), - RocCacheDir::Persistent(cache::roc_cache_packages_dir().as_path()), - load_config, - ); - - let mut loaded = match load_result { - Ok(loaded) => loaded, - Err(LoadMonomorphizedError::LoadingProblem(problem)) => { - return handle_loading_problem(problem); - } - Err(LoadMonomorphizedError::ErrorModule(module)) => { - return handle_error_module(module, start_time.elapsed(), path.as_os_str(), false); + process::exit(1); + } else if path.is_dir() { + find_all_roc_files(path, &mut flatten_paths); + } else { + flatten_paths.push(path.clone()); + } } + flatten_paths }; - let problems = report_problems_monomorphized(&mut loaded); - let mut expectations = std::mem::take(&mut loaded.expectations); + let mut all_files_total_failed_count = 0; + let mut all_files_total_passed_count = 0; - let interns = loaded.interns.clone(); - let sources = loaded.sources.clone(); + for path in paths.iter() { + let arena = &arena; + let function_kind = FunctionKind::from_env(); - let (dyn_lib, expects_by_module, layout_interner) = - roc_repl_expect::run::expect_mono_module_to_dylib( - arena, + let opt_main_path = matches.get_one::(FLAG_MAIN); + + // Step 1: compile the app and generate the .o file + let load_config = LoadConfig { target, - loaded, - opt_level, - LlvmBackendMode::CliTest, - ) - .unwrap(); - - // Print warnings before running tests. - { - debug_assert_eq!( - problems.errors, 0, - "if there were errors, we would have already exited." + function_kind, + // TODO: expose this from CLI? + render: roc_reporting::report::RenderTarget::ColorTerminal, + palette: roc_reporting::report::DEFAULT_PALETTE, + threading, + exec_mode: ExecutionMode::Test, + }; + let load_result = roc_load::load_and_monomorphize( + arena, + path.to_path_buf(), + opt_main_path.cloned(), + RocCacheDir::Persistent(cache::roc_cache_packages_dir().as_path()), + load_config, ); - if problems.warnings > 0 { - problems.print_error_warning_count(start_time.elapsed()); - println!(".\n\nRunning tests…\n\n\x1B[36m{}\x1B[39m", "─".repeat(80)); + + let mut loaded = match load_result { + Ok(loaded) => loaded, + Err(LoadMonomorphizedError::LoadingProblem(problem)) => { + return handle_loading_problem(problem); + } + Err(LoadMonomorphizedError::ErrorModule(module)) => { + return handle_error_module(module, start_time.elapsed(), path.as_os_str(), false); + } + }; + let problems = report_problems_monomorphized(&mut loaded); + + let mut expectations = std::mem::take(&mut loaded.expectations); + + let interns = loaded.interns.clone(); + let sources = loaded.sources.clone(); + + let (dyn_lib, expects_by_module, layout_interner) = + roc_repl_expect::run::expect_mono_module_to_dylib( + arena, + target, + loaded, + opt_level, + LlvmBackendMode::CliTest, + ) + .unwrap(); + + // Print warnings before running tests. + { + debug_assert_eq!( + problems.errors, 0, + "if there were errors, we would have already exited." + ); + if problems.warnings > 0 { + problems.print_error_warning_count(start_time.elapsed()); + println!(".\n\nRunning tests…\n\n\x1B[36m{}\x1B[39m", "─".repeat(80)); + } + } + + // Run the tests. + let arena = &bumpalo::Bump::new(); + let interns = arena.alloc(interns); + + let mut writer = std::io::stdout(); + + let mut total_failed_count = 0; + let mut total_passed_count = 0; + + let mut results_by_module = Vec::new(); + let global_layout_interner = layout_interner.into_global(); + + let compilation_duration = start_time.elapsed(); + + for (module_id, expects) in expects_by_module.into_iter() { + let test_start_time = Instant::now(); + + let (failed_count, passed_count) = roc_repl_expect::run::run_toplevel_expects( + &mut writer, + roc_reporting::report::RenderTarget::ColorTerminal, + arena, + interns, + &global_layout_interner, + &dyn_lib, + &mut expectations, + expects, + ) + .unwrap(); + + let tests_duration = test_start_time.elapsed(); + + results_by_module.push(ModuleTestResults { + module_id, + failed_count, + passed_count, + tests_duration, + }); + + total_failed_count += failed_count; + total_passed_count += passed_count; + } + + let total_duration = start_time.elapsed(); + all_files_total_failed_count += total_failed_count; + all_files_total_passed_count += total_passed_count; + if total_failed_count == 0 && total_passed_count == 0 { + // Only report no expectations found once. + continue; + } else if matches.get_flag(FLAG_VERBOSE) { + println!("Compiled in {} ms.", compilation_duration.as_millis()); + for module_test_results in results_by_module { + print_test_results(module_test_results, &sources); + } + } else { + let test_summary_str = + test_summary(total_failed_count, total_passed_count, total_duration); + println!("{test_summary_str}"); } } - - // Run the tests. - let arena = &bumpalo::Bump::new(); - let interns = arena.alloc(interns); - - let mut writer = std::io::stdout(); - - let mut total_failed_count = 0; - let mut total_passed_count = 0; - - let mut results_by_module = Vec::new(); - let global_layout_interner = layout_interner.into_global(); - - let compilation_duration = start_time.elapsed(); - - for (module_id, expects) in expects_by_module.into_iter() { - let test_start_time = Instant::now(); - - let (failed_count, passed_count) = roc_repl_expect::run::run_toplevel_expects( - &mut writer, - roc_reporting::report::RenderTarget::ColorTerminal, - arena, - interns, - &global_layout_interner, - &dyn_lib, - &mut expectations, - expects, - ) - .unwrap(); - - let tests_duration = test_start_time.elapsed(); - - results_by_module.push(ModuleTestResults { - module_id, - failed_count, - passed_count, - tests_duration, - }); - - total_failed_count += failed_count; - total_passed_count += passed_count; - } - - let total_duration = start_time.elapsed(); - - if total_failed_count == 0 && total_passed_count == 0 { + if all_files_total_failed_count == 0 && all_files_total_passed_count == 0 { // TODO print this in a more nicely formatted way! println!("No expectations were found."); @@ -655,18 +684,32 @@ pub fn test(matches: &ArgMatches, target: Target) -> io::Result { // running tests altogether! Ok(2) } else { - if matches.get_flag(FLAG_VERBOSE) { - println!("Compiled in {} ms.", compilation_duration.as_millis()); - for module_test_results in results_by_module { - print_test_results(module_test_results, &sources); - } - } else { - let test_summary_str = - test_summary(total_failed_count, total_passed_count, total_duration); - println!("{test_summary_str}"); - } + Ok((all_files_total_failed_count > 0) as i32) + } +} - Ok((total_failed_count > 0) as i32) +fn find_all_roc_files(path: &PathBuf, flatten_paths: &mut Vec) { + if path.is_dir() { + if let Ok(entries) = std::fs::read_dir(path) { + entries.for_each(|entry| { + if let Ok(entry) = entry { + let entry_path = entry.path(); + find_all_roc_files(&entry_path, flatten_paths); + } + }); + } else { + eprintln!( + "\nSomething went wrong opening the directory {}\n", + path.display() + ); + } + } else if path.is_file() { + match path.extension() { + Some(extension) if extension == "roc" => { + flatten_paths.push(path.clone()); + } + _ => {} + } } } diff --git a/crates/compiler/build/src/program.rs b/crates/compiler/build/src/program.rs index 9b3a5a5c44..57b5045d52 100644 --- a/crates/compiler/build/src/program.rs +++ b/crates/compiler/build/src/program.rs @@ -525,7 +525,7 @@ fn gen_from_mono_module_dev_wasm32<'a>( let host_bytes = std::fs::read(built_host_path).unwrap_or_else(|_| { internal_error!( - "Failed to read host object file {}! Try omitting --prebuilt-platform", + "Failed to read host object file {}!", built_host_path.display() ) }); diff --git a/crates/compiler/can/src/annotation.rs b/crates/compiler/can/src/annotation.rs index 090ce2d35a..5ade8cb07f 100644 --- a/crates/compiler/can/src/annotation.rs +++ b/crates/compiler/can/src/annotation.rs @@ -482,7 +482,6 @@ pub fn find_type_def_symbols( AssignedField::LabelOnly(_) => {} AssignedField::SpaceBefore(inner, _) | AssignedField::SpaceAfter(inner, _) => inner_stack.push(inner), - AssignedField::Malformed(_) => {} } } @@ -507,7 +506,6 @@ pub fn find_type_def_symbols( Tag::SpaceBefore(inner, _) | Tag::SpaceAfter(inner, _) => { inner_stack.push(inner) } - Tag::Malformed(_) => {} } } @@ -1355,7 +1353,7 @@ fn can_assigned_fields<'a>( // field names we've seen so far in this record let mut seen = std::collections::HashMap::with_capacity(fields.len()); - 'outer: for loc_field in fields.iter() { + for loc_field in fields.iter() { let mut field = &loc_field.value; // use this inner loop to unwrap the SpaceAfter/SpaceBefore @@ -1430,12 +1428,6 @@ fn can_assigned_fields<'a>( field = nested; continue 'inner; } - Malformed(string) => { - malformed(env, region, string); - - // completely skip this element, advance to the next tag - continue 'outer; - } } }; @@ -1522,7 +1514,7 @@ fn can_tags<'a>( // tag names we've seen so far in this tag union let mut seen = std::collections::HashMap::with_capacity(tags.len()); - 'outer: for loc_tag in tags.iter() { + for loc_tag in tags.iter() { let mut tag = &loc_tag.value; // use this inner loop to unwrap the SpaceAfter/SpaceBefore @@ -1561,12 +1553,6 @@ fn can_tags<'a>( tag = nested; continue 'inner; } - Tag::Malformed(string) => { - malformed(env, region, string); - - // completely skip this element, advance to the next tag - continue 'outer; - } } }; diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index a10fd50dd8..36b5e065b2 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -715,10 +715,6 @@ fn canonicalize_claimed_ability_impl<'a>( }); Err(()) } - AssignedField::Malformed(_) => { - // An error will already have been reported - Err(()) - } AssignedField::SpaceBefore(_, _) | AssignedField::SpaceAfter(_, _) | AssignedField::IgnoredValue(_, _, _) => { diff --git a/crates/compiler/can/src/desugar.rs b/crates/compiler/can/src/desugar.rs index c28d3f7682..b091d406c4 100644 --- a/crates/compiler/can/src/desugar.rs +++ b/crates/compiler/can/src/desugar.rs @@ -407,7 +407,6 @@ pub fn desugar_expr<'a>( | AccessorFunction(_) | Underscore { .. } | MalformedIdent(_, _) - | MalformedClosure | MalformedSuffixed(..) | PrecedenceConflict { .. } | EmptyRecordBuilder(_) @@ -712,7 +711,6 @@ pub fn desugar_expr<'a>( AssignedField::SpaceBefore(_, _) | AssignedField::SpaceAfter(_, _) => { unreachable!("Should have been desugared in `desugar_field`") } - AssignedField::Malformed(_name) => continue, }; field_data.push(FieldData { @@ -1316,8 +1314,6 @@ fn desugar_field<'a>( } SpaceBefore(field, _spaces) => desugar_field(env, scope, field), SpaceAfter(field, _spaces) => desugar_field(env, scope, field), - - Malformed(string) => Malformed(string), } } diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index 1e619cc503..7d00fbc719 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -1391,10 +1391,6 @@ pub fn canonicalize_expr<'a>( Output::default(), ) } - ast::Expr::MalformedClosure => { - use roc_problem::can::RuntimeError::*; - (RuntimeError(MalformedClosure(region)), Output::default()) - } ast::Expr::MalformedIdent(name, bad_ident) => { use roc_problem::can::RuntimeError::*; @@ -1976,10 +1972,6 @@ fn canonicalize_field<'a>( SpaceBefore(sub_field, _) | SpaceAfter(sub_field, _) => { canonicalize_field(env, var_store, scope, sub_field) } - - Malformed(_string) => { - internal_error!("TODO canonicalize malformed record field"); - } } } @@ -2568,8 +2560,7 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool { | ast::Expr::Underscore(_) | ast::Expr::MalformedIdent(_, _) | ast::Expr::Tag(_) - | ast::Expr::OpaqueRef(_) - | ast::Expr::MalformedClosure => true, + | ast::Expr::OpaqueRef(_) => true, // Newlines are disallowed inside interpolation, and these all require newlines ast::Expr::DbgStmt(_, _) | ast::Expr::LowLevelDbg(_, _, _) @@ -2604,7 +2595,7 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool { | ast::AssignedField::IgnoredValue(_label, loc_comments, loc_val) => { loc_comments.is_empty() && is_valid_interpolation(&loc_val.value) } - ast::AssignedField::Malformed(_) | ast::AssignedField::LabelOnly(_) => true, + ast::AssignedField::LabelOnly(_) => true, ast::AssignedField::SpaceBefore(_, _) | ast::AssignedField::SpaceAfter(_, _) => false, }), ast::Expr::Tuple(fields) => fields @@ -2655,7 +2646,7 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool { | ast::AssignedField::IgnoredValue(_label, loc_comments, loc_val) => { loc_comments.is_empty() && is_valid_interpolation(&loc_val.value) } - ast::AssignedField::Malformed(_) | ast::AssignedField::LabelOnly(_) => true, + ast::AssignedField::LabelOnly(_) => true, ast::AssignedField::SpaceBefore(_, _) | ast::AssignedField::SpaceAfter(_, _) => false, }) @@ -2668,7 +2659,7 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool { | ast::AssignedField::IgnoredValue(_label, loc_comments, loc_val) => { loc_comments.is_empty() && is_valid_interpolation(&loc_val.value) } - ast::AssignedField::Malformed(_) | ast::AssignedField::LabelOnly(_) => true, + ast::AssignedField::LabelOnly(_) => true, ast::AssignedField::SpaceBefore(_, _) | ast::AssignedField::SpaceAfter(_, _) => false, }) diff --git a/crates/compiler/fmt/src/annotation.rs b/crates/compiler/fmt/src/annotation.rs index e79e2463f9..3bf76938ca 100644 --- a/crates/compiler/fmt/src/annotation.rs +++ b/crates/compiler/fmt/src/annotation.rs @@ -438,7 +438,6 @@ fn is_multiline_assigned_field_help(afield: &AssignedField<'_, T | IgnoredValue(_, spaces, ann) => !spaces.is_empty() || ann.value.is_multiline(), LabelOnly(_) => false, AssignedField::SpaceBefore(_, _) | AssignedField::SpaceAfter(_, _) => true, - Malformed(text) => text.chars().any(|c| c == '\n'), } } @@ -522,9 +521,6 @@ fn format_assigned_field_help( format_assigned_field_help(sub_field, buf, indent, separator_spaces, is_multiline); fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent); } - Malformed(raw) => { - buf.push_str(raw); - } } } @@ -535,7 +531,6 @@ impl<'a> Formattable for Tag<'a> { match self { Apply { args, .. } => args.iter().any(|arg| arg.value.is_multiline()), Tag::SpaceBefore(_, _) | Tag::SpaceAfter(_, _) => true, - Malformed(text) => text.chars().any(|c| c == '\n'), } } @@ -572,10 +567,6 @@ impl<'a> Formattable for Tag<'a> { } } Tag::SpaceBefore(_, _) | Tag::SpaceAfter(_, _) => unreachable!(), - Tag::Malformed(raw) => { - buf.indent(indent); - buf.push_str(raw); - } } } } diff --git a/crates/compiler/fmt/src/expr.rs b/crates/compiler/fmt/src/expr.rs index 62911b62bc..26d892f329 100644 --- a/crates/compiler/fmt/src/expr.rs +++ b/crates/compiler/fmt/src/expr.rs @@ -44,7 +44,6 @@ impl<'a> Formattable for Expr<'a> { | Var { .. } | Underscore { .. } | MalformedIdent(_, _) - | MalformedClosure | Tag(_) | OpaqueRef(_) | Crash @@ -557,7 +556,6 @@ impl<'a> Formattable for Expr<'a> { buf.indent(indent); loc_expr.format_with_options(buf, parens, newlines, indent); } - MalformedClosure => {} PrecedenceConflict { .. } => {} EmptyRecordBuilder { .. } => {} SingleFieldRecordBuilder { .. } => {} @@ -1646,9 +1644,6 @@ fn format_assigned_field_multiline( format_assigned_field_multiline(buf, sub_field, indent, separator_prefix); fmt_comments_only(buf, spaces.iter(), NewlineAt::Top, indent); } - Malformed(raw) => { - buf.push_str(raw); - } } } diff --git a/crates/compiler/load_internal/src/docs.rs b/crates/compiler/load_internal/src/docs.rs index a2dd011baa..c9d7f78ee8 100644 --- a/crates/compiler/load_internal/src/docs.rs +++ b/crates/compiler/load_internal/src/docs.rs @@ -476,7 +476,7 @@ fn contains_unexposed_type( return true; } } - AssignedField::Malformed(_) | AssignedField::LabelOnly(_) => { + AssignedField::LabelOnly(_) => { // contains no unexposed types, so continue } AssignedField::SpaceBefore(field, _) | AssignedField::SpaceAfter(field, _) => { @@ -524,9 +524,6 @@ fn contains_unexposed_type( } } } - Tag::Malformed(_) => { - // contains no unexposed types, so continue - } Tag::SpaceBefore(tag, _) | Tag::SpaceAfter(tag, _) => { tags_to_process.push(*tag); } @@ -728,7 +725,7 @@ fn record_field_to_doc( AssignedField::LabelOnly(label) => Some(RecordField::LabelOnly { name: label.value.to_string(), }), - AssignedField::Malformed(_) | AssignedField::IgnoredValue(_, _, _) => None, + AssignedField::IgnoredValue(_, _, _) => None, } } @@ -749,7 +746,6 @@ fn tag_to_doc(in_func_ann: bool, tag: ast::Tag) -> Option { }), ast::Tag::SpaceBefore(&sub_tag, _) => tag_to_doc(in_func_ann, sub_tag), ast::Tag::SpaceAfter(&sub_tag, _) => tag_to_doc(in_func_ann, sub_tag), - ast::Tag::Malformed(_) => None, } } diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index 870d176913..585ff0a3c6 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -68,7 +68,7 @@ use roc_solve_problem::TypeError; use roc_target::Target; use roc_types::subs::{CopiedImport, ExposedTypesStorageSubs, Subs, VarStore, Variable}; use roc_types::types::{Alias, Types}; -use roc_worker::{ChannelProblem, WorkerMsg}; +use roc_worker::ChannelProblem; use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::collections::HashMap; use std::io; @@ -1035,14 +1035,14 @@ type MsgSender<'a> = Sender>; /// Add a task to the queue, and notify all the listeners. fn enqueue_task<'a>( injector: &Injector>, - listeners: &[Sender], + listeners: &[Sender<()>], task: BuildTask<'a>, ) -> Result<(), LoadingProblem<'a>> { injector.push(task); for listener in listeners { listener - .send(WorkerMsg::TaskAdded) + .send(()) .map_err(|_| LoadingProblem::ChannelProblem(ChannelProblem::FailedToEnqueueTask))?; } @@ -1569,9 +1569,9 @@ pub fn load_single_threaded<'a>( // We'll add tasks to this, and then worker threads will take tasks from it. let injector = Injector::new(); - let (worker_msg_tx, worker_msg_rx) = bounded(1024); - let worker_listener = worker_msg_tx; - let worker_listeners = arena.alloc([worker_listener]); + let (worker_wakup_tx, worker_wakup_rx) = bounded(1024); + let worker_waker = worker_wakup_tx; + let worker_wakers = [worker_waker]; let worker = Worker::new_fifo(); let stealer = worker.stealer(); @@ -1579,7 +1579,7 @@ pub fn load_single_threaded<'a>( // now we just manually interleave stepping the state "thread" and the worker "thread" loop { - match state_thread_step(arena, state, worker_listeners, &injector, &msg_tx, &msg_rx) { + match state_thread_step(arena, state, &worker_wakers, &injector, &msg_tx, &msg_rx) { Ok(ControlFlow::Break(done)) => return Ok(done), Ok(ControlFlow::Continue(new_state)) => { state = new_state; @@ -1589,7 +1589,7 @@ pub fn load_single_threaded<'a>( // then check if the worker can step let control_flow = - roc_worker::worker_task_step(&worker, &injector, stealers, &worker_msg_rx, |task| { + roc_worker::worker_task_step(&worker, &injector, stealers, &worker_wakup_rx, |task| { run_task(task, arena, &src_dir, msg_tx.clone(), roc_cache_dir, target) }); @@ -1606,7 +1606,7 @@ pub fn load_single_threaded<'a>( fn state_thread_step<'a>( arena: &'a Bump, state: State<'a>, - worker_listeners: &'a [Sender], + worker_wakers: &[Sender<()>], injector: &Injector>, msg_tx: &crossbeam::channel::Sender>, msg_rx: &crossbeam::channel::Receiver>, @@ -1712,14 +1712,8 @@ fn state_thread_step<'a>( let render = state.render; let palette = state.palette; - let res_state = update( - state, - msg, - msg_tx.clone(), - injector, - worker_listeners, - arena, - ); + let res_state = + update(state, msg, msg_tx.clone(), injector, worker_wakers, arena); match res_state { Ok(new_state) => Ok(ControlFlow::Continue(new_state)), @@ -1993,15 +1987,21 @@ fn load_multi_threaded<'a>( { let thread_result = thread::scope(|thread_scope| { - let mut worker_listeners = - bumpalo::collections::Vec::with_capacity_in(num_workers, arena); + // Careful! It's important that worker listeners aren't allocated in the arena, + // since they need to be correctly dropped if we have a panic in this thread::scope code. + // Making sure they're owned means they'll be dropped correctly on either normal exit + // of this thread::scope block or on panicking. When they're dropped, the worker threads + // will correctly exit their message processing loops. + // If these were allocated in the arena, we might panic without shutting down the worker threads, + // causing the thread::scope block to hang while it waits for the worker threads to exit. + let mut worker_wakers = Vec::with_capacity(num_workers); for worker_arena in it { let msg_tx = msg_tx.clone(); let worker = worker_queues.pop().unwrap(); - let (worker_msg_tx, worker_msg_rx) = bounded(1024); - worker_listeners.push(worker_msg_tx); + let (worker_wakup_tx, worker_wakup_rx) = bounded(1024); + worker_wakers.push(worker_wakup_tx); // We only want to move a *reference* to the main task queue's // injector in the thread, not the injector itself @@ -2015,16 +2015,22 @@ fn load_multi_threaded<'a>( .stack_size(EXPANDED_STACK_SIZE) .spawn(move |_| { // will process messages until we run out - roc_worker::worker_task(worker, injector, stealers, worker_msg_rx, |task| { - run_task( - task, - worker_arena, - src_dir, - msg_tx.clone(), - roc_cache_dir, - target, - ) - }) + roc_worker::worker_task( + worker, + injector, + stealers, + worker_wakup_rx, + |task| { + run_task( + task, + worker_arena, + src_dir, + msg_tx.clone(), + roc_cache_dir, + target, + ) + }, + ) }); res_join_handle.unwrap_or_else(|_| { @@ -2039,31 +2045,13 @@ fn load_multi_threaded<'a>( // Grab a reference to these Senders outside the loop, so we can share // it across each iteration of the loop. - let worker_listeners = worker_listeners.into_bump_slice(); let msg_tx = msg_tx.clone(); - macro_rules! shut_down_worker_threads { - () => { - for listener in worker_listeners { - // We intentionally don't propagate this Result, because even if - // shutting down a worker failed (which can happen if a a panic - // occurred on that thread), we want to continue shutting down - // the others regardless. - if listener.send(WorkerMsg::Shutdown).is_err() { - log!("There was an error trying to shutdown a worker thread. One reason this can happen is if the thread panicked."); - } - } - }; - } - // The root module will have already queued up messages to process, // and processing those messages will in turn queue up more messages. loop { - match state_thread_step(arena, state, worker_listeners, &injector, &msg_tx, &msg_rx) - { + match state_thread_step(arena, state, &worker_wakers, &injector, &msg_tx, &msg_rx) { Ok(ControlFlow::Break(load_result)) => { - shut_down_worker_threads!(); - return Ok(load_result); } Ok(ControlFlow::Continue(new_state)) => { @@ -2071,8 +2059,6 @@ fn load_multi_threaded<'a>( continue; } Err(e) => { - shut_down_worker_threads!(); - return Err(e); } } @@ -2111,13 +2097,13 @@ fn start_tasks<'a>( state: &mut State<'a>, work: MutSet<(ModuleId, Phase)>, injector: &Injector>, - worker_listeners: &'a [Sender], + worker_wakers: &[Sender<()>], ) -> Result<(), LoadingProblem<'a>> { for (module_id, phase) in work { let tasks = start_phase(module_id, phase, arena, state); for task in tasks { - enqueue_task(injector, worker_listeners, task)? + enqueue_task(injector, worker_wakers, task)? } } @@ -2179,7 +2165,7 @@ fn update<'a>( msg: Msg<'a>, msg_tx: MsgSender<'a>, injector: &Injector>, - worker_listeners: &'a [Sender], + worker_wakers: &[Sender<()>], arena: &'a Bump, ) -> Result, LoadingProblem<'a>> { use self::Msg::*; @@ -2305,7 +2291,7 @@ fn update<'a>( work.extend(state.dependencies.notify(home, Phase::LoadHeader)); work.insert((home, Phase::Parse)); - start_tasks(arena, &mut state, work, injector, worker_listeners)?; + start_tasks(arena, &mut state, work, injector, worker_wakers)?; Ok(state) } @@ -2382,7 +2368,7 @@ fn update<'a>( } }; - start_tasks(arena, &mut state, work, injector, worker_listeners)?; + start_tasks(arena, &mut state, work, injector, worker_wakers)?; state .module_cache @@ -2393,7 +2379,7 @@ fn update<'a>( let work = state.dependencies.notify(module_id, Phase::Parse); - start_tasks(arena, &mut state, work, injector, worker_listeners)?; + start_tasks(arena, &mut state, work, injector, worker_wakers)?; Ok(state) } @@ -2445,7 +2431,7 @@ fn update<'a>( .dependencies .notify(module_id, Phase::CanonicalizeAndConstrain); - start_tasks(arena, &mut state, work, injector, worker_listeners)?; + start_tasks(arena, &mut state, work, injector, worker_wakers)?; Ok(state) } @@ -2652,7 +2638,7 @@ fn update<'a>( work }; - start_tasks(arena, &mut state, work, injector, worker_listeners)?; + start_tasks(arena, &mut state, work, injector, worker_wakers)?; } Ok(state) @@ -2700,7 +2686,7 @@ fn update<'a>( .dependencies .notify(module_id, Phase::FindSpecializations); - start_tasks(arena, &mut state, work, injector, worker_listeners)?; + start_tasks(arena, &mut state, work, injector, worker_wakers)?; Ok(state) } @@ -2990,13 +2976,13 @@ fn update<'a>( let work = state.dependencies.reload_make_specialization_pass(); - start_tasks(arena, &mut state, work, injector, worker_listeners)?; + start_tasks(arena, &mut state, work, injector, worker_wakers)?; Ok(state) } NextStep::MakingInPhase => { - start_tasks(arena, &mut state, work, injector, worker_listeners)?; + start_tasks(arena, &mut state, work, injector, worker_wakers)?; Ok(state) } diff --git a/crates/compiler/parse/src/ast.rs b/crates/compiler/parse/src/ast.rs index 34d83e84b7..8c30da1e56 100644 --- a/crates/compiler/parse/src/ast.rs +++ b/crates/compiler/parse/src/ast.rs @@ -539,7 +539,6 @@ pub enum Expr<'a> { // Problems MalformedIdent(&'a str, crate::ident::BadIdent), - MalformedClosure, MalformedSuffixed(&'a Loc>), // Both operators were non-associative, e.g. (True == False == False). // We should tell the author to disambiguate by grouping them with parens. @@ -687,7 +686,6 @@ pub fn is_expr_suffixed(expr: &Expr) -> bool { Expr::SpaceBefore(a, _) => is_expr_suffixed(a), Expr::SpaceAfter(a, _) => is_expr_suffixed(a), Expr::MalformedIdent(_, _) => false, - Expr::MalformedClosure => false, Expr::MalformedSuffixed(_) => false, Expr::PrecedenceConflict(_) => false, Expr::EmptyRecordBuilder(_) => false, @@ -713,7 +711,6 @@ fn is_assigned_value_suffixed<'a>(value: &AssignedField<'a, Expr<'a>>) -> bool { AssignedField::SpaceBefore(a, _) | AssignedField::SpaceAfter(a, _) => { is_assigned_value_suffixed(a) } - AssignedField::Malformed(_) => false, } } @@ -889,7 +886,7 @@ impl<'a, 'b> RecursiveValueDefIter<'a, 'b> { | OptionalValue(_, _, loc_val) | IgnoredValue(_, _, loc_val) => break expr_stack.push(&loc_val.value), SpaceBefore(next, _) | SpaceAfter(next, _) => current = *next, - LabelOnly(_) | Malformed(_) => break, + LabelOnly(_) => break, } } } @@ -1038,7 +1035,6 @@ impl<'a, 'b> RecursiveValueDefIter<'a, 'b> { | Tag(_) | OpaqueRef(_) | MalformedIdent(_, _) - | MalformedClosure | PrecedenceConflict(_) | MalformedSuffixed(_) => { /* terminal */ } } @@ -1614,9 +1610,6 @@ pub enum Tag<'a> { // We preserve this for the formatter; canonicalization ignores it. SpaceBefore(&'a Tag<'a>, &'a [CommentOrNewline<'a>]), SpaceAfter(&'a Tag<'a>, &'a [CommentOrNewline<'a>]), - - /// A malformed tag, which will code gen to a runtime error - Malformed(&'a str), } #[derive(Debug, Clone, Copy, PartialEq)] @@ -1639,9 +1632,6 @@ pub enum AssignedField<'a, Val> { // We preserve this for the formatter; canonicalization ignores it. SpaceBefore(&'a AssignedField<'a, Val>, &'a [CommentOrNewline<'a>]), SpaceAfter(&'a AssignedField<'a, Val>, &'a [CommentOrNewline<'a>]), - - /// A malformed assigned field, which will code gen to a runtime error - Malformed(&'a str), } impl<'a, Val> AssignedField<'a, Val> { @@ -1653,7 +1643,7 @@ impl<'a, Val> AssignedField<'a, Val> { Self::RequiredValue(_, _, val) | Self::OptionalValue(_, _, val) | Self::IgnoredValue(_, _, val) => break Some(val), - Self::LabelOnly(_) | Self::Malformed(_) => break None, + Self::LabelOnly(_) => break None, Self::SpaceBefore(next, _) | Self::SpaceAfter(next, _) => current = *next, } } @@ -2518,7 +2508,6 @@ impl<'a> Malformed for Expr<'a> { ParensAround(expr) => expr.is_malformed(), MalformedIdent(_, _) | - MalformedClosure | MalformedSuffixed(..) | PrecedenceConflict(_) | EmptyRecordBuilder(_) | @@ -2593,7 +2582,6 @@ impl<'a, T: Malformed> Malformed for AssignedField<'a, T> { AssignedField::SpaceBefore(field, _) | AssignedField::SpaceAfter(field, _) => { field.is_malformed() } - AssignedField::Malformed(_) => true, } } } @@ -2815,7 +2803,6 @@ impl<'a> Malformed for Tag<'a> { match self { Tag::Apply { name: _, args } => args.iter().any(|arg| arg.is_malformed()), Tag::SpaceBefore(tag, _) | Tag::SpaceAfter(tag, _) => tag.is_malformed(), - Tag::Malformed(_) => true, } } } diff --git a/crates/compiler/parse/src/expr.rs b/crates/compiler/parse/src/expr.rs index d656bf09bf..f351c00c4c 100644 --- a/crates/compiler/parse/src/expr.rs +++ b/crates/compiler/parse/src/expr.rs @@ -2180,7 +2180,6 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result( arena.alloc(assigned_expr_field_to_pattern_help(arena, nested)?), spaces, ), - AssignedField::Malformed(string) => Pattern::Malformed(string), AssignedField::IgnoredValue(_, _, _) => return Err(()), }) } diff --git a/crates/compiler/parse/src/normalize.rs b/crates/compiler/parse/src/normalize.rs index 8151400c08..0eaa687998 100644 --- a/crates/compiler/parse/src/normalize.rs +++ b/crates/compiler/parse/src/normalize.rs @@ -556,7 +556,6 @@ impl<'a, T: Normalize<'a> + Copy + std::fmt::Debug> Normalize<'a> for AssignedFi arena.alloc(c.normalize(arena)), ), AssignedField::LabelOnly(a) => AssignedField::LabelOnly(a.normalize(arena)), - AssignedField::Malformed(a) => AssignedField::Malformed(a), AssignedField::SpaceBefore(a, _) => a.normalize(arena), AssignedField::SpaceAfter(a, _) => a.normalize(arena), } @@ -784,7 +783,6 @@ impl<'a> Normalize<'a> for Expr<'a> { a.normalize(arena) } Expr::MalformedIdent(a, b) => Expr::MalformedIdent(a, remove_spaces_bad_ident(b)), - Expr::MalformedClosure => Expr::MalformedClosure, Expr::MalformedSuffixed(a) => Expr::MalformedSuffixed(a), Expr::PrecedenceConflict(a) => Expr::PrecedenceConflict(a), Expr::SpaceBefore(a, _) => a.normalize(arena), @@ -938,7 +936,6 @@ impl<'a> Normalize<'a> for Tag<'a> { name: name.normalize(arena), args: args.normalize(arena), }, - Tag::Malformed(a) => Tag::Malformed(a), Tag::SpaceBefore(a, _) => a.normalize(arena), Tag::SpaceAfter(a, _) => a.normalize(arena), } diff --git a/crates/compiler/problem/src/can.rs b/crates/compiler/problem/src/can.rs index 85de1de06b..73496278f7 100644 --- a/crates/compiler/problem/src/can.rs +++ b/crates/compiler/problem/src/can.rs @@ -435,7 +435,6 @@ impl Problem { | Problem::RuntimeError(RuntimeError::InvalidPrecedence(_, region)) | Problem::RuntimeError(RuntimeError::MalformedIdentifier(_, _, region)) | Problem::RuntimeError(RuntimeError::MalformedTypeName(_, region)) - | Problem::RuntimeError(RuntimeError::MalformedClosure(region)) | Problem::RuntimeError(RuntimeError::MalformedSuffixed(region)) | Problem::RuntimeError(RuntimeError::InvalidRecordUpdate { region }) | Problem::RuntimeError(RuntimeError::InvalidFloat(_, region, _)) @@ -678,7 +677,6 @@ pub enum RuntimeError { InvalidPrecedence(PrecedenceProblem, Region), MalformedIdentifier(Box, roc_parse::ident::BadIdent, Region), MalformedTypeName(Box, Region), - MalformedClosure(Region), InvalidRecordUpdate { region: Region, }, @@ -750,7 +748,6 @@ impl RuntimeError { | RuntimeError::InvalidPrecedence(_, region) | RuntimeError::MalformedIdentifier(_, _, region) | RuntimeError::MalformedTypeName(_, region) - | RuntimeError::MalformedClosure(region) | RuntimeError::MalformedSuffixed(region) | RuntimeError::InvalidRecordUpdate { region } | RuntimeError::InvalidFloat(_, region, _) diff --git a/crates/compiler/worker/src/worker.rs b/crates/compiler/worker/src/worker.rs index 71cb267be9..8c9d2bcd60 100644 --- a/crates/compiler/worker/src/worker.rs +++ b/crates/compiler/worker/src/worker.rs @@ -7,12 +7,6 @@ use roc_module::symbol::ModuleId; use roc_work::Phase; use std::ops::ControlFlow; -#[derive(Debug)] -pub enum WorkerMsg { - Shutdown, - TaskAdded, -} - #[derive(Debug)] pub enum ChannelProblem { FailedToSendRootMsg, @@ -29,41 +23,31 @@ pub fn worker_task_step( worker: &Worker, injector: &Injector, stealers: &[Stealer], - worker_msg_rx: &Receiver, + worker_wakeup_rx: &Receiver<()>, run_task: impl Fn(Task) -> Result<(), ChannelProblem>, ) -> Result, ChannelProblem> { - match worker_msg_rx.try_recv() { - Ok(msg) => { - match msg { - WorkerMsg::Shutdown => { - // We've finished all our work. It's time to - // shut down the thread, so when the main thread - // blocks on joining with all the worker threads, - // it can finally exit too! - Ok(ControlFlow::Break(())) - } - WorkerMsg::TaskAdded => { - // Find a task - either from this thread's queue, - // or from the main queue, or from another worker's - // queue - and run it. - // - // There might be no tasks to work on! That could - // happen if another thread is working on a task - // which will later result in more tasks being - // added. In that case, do nothing, and keep waiting - // until we receive a Shutdown message. - if let Some(task) = find_task(worker, injector, stealers) { - run_task(task)?; - } - - Ok(ControlFlow::Continue(())) - } + match worker_wakeup_rx.try_recv() { + Ok(()) => { + // Find a task - either from this thread's queue, + // or from the main queue, or from another worker's + // queue - and run it. + // + // There might be no tasks to work on! That could + // happen if another thread is working on a task + // which will later result in more tasks being + // added. In that case, do nothing, and keep waiting + // until we receive a Shutdown message. + if let Some(task) = find_task(worker, injector, stealers) { + run_task(task)?; } + + Ok(ControlFlow::Continue(())) } Err(err) => match err { crossbeam::channel::TryRecvError::Empty => Ok(ControlFlow::Continue(())), crossbeam::channel::TryRecvError::Disconnected => { - Err(ChannelProblem::ChannelDisconnected) + // The channel sender has been dropped, which means we want to shut down + Ok(ControlFlow::Break(())) } }, } @@ -73,33 +57,22 @@ pub fn worker_task( worker: Worker, injector: &Injector, stealers: &[Stealer], - worker_msg_rx: crossbeam::channel::Receiver, + worker_wakeup_rx: crossbeam::channel::Receiver<()>, run_task: impl Fn(Task) -> Result<(), ChannelProblem>, ) -> Result<(), ChannelProblem> { // Keep listening until we receive a Shutdown msg - for msg in worker_msg_rx.iter() { - match msg { - WorkerMsg::Shutdown => { - // We've finished all our work. It's time to - // shut down the thread, so when the main thread - // blocks on joining with all the worker threads, - // it can finally exit too! - return Ok(()); - } - WorkerMsg::TaskAdded => { - // Find a task - either from this thread's queue, - // or from the main queue, or from another worker's - // queue - and run it. - // - // There might be no tasks to work on! That could - // happen if another thread is working on a task - // which will later result in more tasks being - // added. In that case, do nothing, and keep waiting - // until we receive a Shutdown message. - if let Some(task) = find_task(&worker, injector, stealers) { - run_task(task)?; - } - } + for () in worker_wakeup_rx.iter() { + // Find a task - either from this thread's queue, + // or from the main queue, or from another worker's + // queue - and run it. + // + // There might be no tasks to work on! That could + // happen if another thread is working on a task + // which will later result in more tasks being + // added. In that case, do nothing, and keep waiting + // until we receive a Shutdown message. + if let Some(task) = find_task(&worker, injector, stealers) { + run_task(task)?; } } @@ -110,17 +83,17 @@ pub fn start_tasks>( state: &mut State, work: MutSet<(ModuleId, Phase)>, injector: &Injector, - worker_listeners: &[Sender], + worker_wakers: &[Sender<()>], mut start_phase: impl FnMut(ModuleId, Phase, &mut State) -> Tasks, -) -> Result<(), SendError> { +) -> Result<(), SendError<()>> { for (module_id, phase) in work { let tasks = start_phase(module_id, phase, state); for task in tasks { injector.push(task); - for listener in worker_listeners { - listener.send(WorkerMsg::TaskAdded)?; + for listener in worker_wakers { + listener.send(())?; } } } diff --git a/crates/docs/src/static/styles.css b/crates/docs/src/static/styles.css index 3b426ffd81..5c56e37d3e 100644 --- a/crates/docs/src/static/styles.css +++ b/crates/docs/src/static/styles.css @@ -481,7 +481,7 @@ pre>samp { opacity: 1; } -#module-search-form:focus-within #search-type-ahead { +#search-type-ahead { display: flex; gap: 20px; flex-direction: column; @@ -489,11 +489,7 @@ pre>samp { top: calc(var(--module-search-padding-height) + var(--module-search-height)); left: var(--module-search-form-padding-width); width: calc(100% - 2 * var(--module-search-form-padding-width)); -} - -#search-type-ahead { box-sizing: border-box; - display: none; z-index: 100; background-color: var(--body-bg-color); border-width: 1px; diff --git a/crates/language_server/src/analysis/tokens.rs b/crates/language_server/src/analysis/tokens.rs index 6e4035ca6b..f9229bd0c3 100644 --- a/crates/language_server/src/analysis/tokens.rs +++ b/crates/language_server/src/analysis/tokens.rs @@ -443,7 +443,6 @@ where AssignedField::SpaceBefore(af, _) | AssignedField::SpaceAfter(af, _) => { af.iter_tokens(arena) } - AssignedField::Malformed(_) => bumpvec![in arena;], } } } @@ -461,7 +460,6 @@ impl IterTokens for Tag<'_> { .chain(args.iter_tokens(arena)) .collect_in(arena), Tag::SpaceBefore(t, _) | Tag::SpaceAfter(t, _) => t.iter_tokens(arena), - Tag::Malformed(_) => bumpvec![in arena;], } } } @@ -735,7 +733,6 @@ impl IterTokens for Loc> { Expr::SingleFieldRecordBuilder(e) => e.iter_tokens(arena), Expr::OptionalFieldInRecordBuilder(_name, e) => e.iter_tokens(arena), Expr::MalformedIdent(_, _) - | Expr::MalformedClosure | Expr::PrecedenceConflict(_) | Expr::MalformedSuffixed(_) => { bumpvec![in arena;] diff --git a/crates/packaging/src/cache.rs b/crates/packaging/src/cache.rs index 6040328d6a..d819cece5a 100644 --- a/crates/packaging/src/cache.rs +++ b/crates/packaging/src/cache.rs @@ -55,7 +55,6 @@ fn nixos_error_if_dynamic(url: &str, dest_dir: &Path) { You can:\n\n\t\ - Download the source of the platform and build it locally, like in this example:\n\t \ https://github.com/roc-lang/roc/blob/main/examples/platform-switching/rocLovesRust.roc.\n\t \ - When building your roc application, you can use the flag `--prebuilt-platform` to prevent the platform from being rebuilt every time.\n\t \ For some graphical platforms you may need to use https://github.com/guibou/nixGL.\n\n\t\ - Contact the author of the platform to ask them to statically link their platform.\n\t \ musl can be used to prevent a dynamic dependency on the systems' libc.\n\t \ diff --git a/crates/repl_eval/src/gen.rs b/crates/repl_eval/src/gen.rs index f452383255..296e8222fe 100644 --- a/crates/repl_eval/src/gen.rs +++ b/crates/repl_eval/src/gen.rs @@ -22,7 +22,7 @@ pub struct ReplOutput { pub fn format_answer<'a>(arena: &'a Bump, answer: Expr<'_>) -> &'a str { match answer { - Expr::Closure(_, _) | Expr::MalformedClosure => "", + Expr::Closure(_, _) => "", _ => { let mut expr = roc_fmt::Buf::new_in(arena); diff --git a/crates/reporting/src/error/canonicalize.rs b/crates/reporting/src/error/canonicalize.rs index 9591923536..eed1ee9b03 100644 --- a/crates/reporting/src/error/canonicalize.rs +++ b/crates/reporting/src/error/canonicalize.rs @@ -2194,9 +2194,6 @@ fn pretty_runtime_error<'b>( title = SYNTAX_PROBLEM; } - RuntimeError::MalformedClosure(_) => { - todo!(""); - } RuntimeError::MalformedSuffixed(_) => { todo!("error for malformed suffix"); } diff --git a/www/content/install/getting_started.md b/www/content/install/getting_started.md index 714e7b1c50..b8631c63cf 100644 --- a/www/content/install/getting_started.md +++ b/www/content/install/getting_started.md @@ -29,15 +29,14 @@ If you have a specific question, the [FAQ](/faq) might have an answer, although ## Running Examples -You can run examples as follows: +You can run [examples](https://github.com/roc-lang/examples) as follows: ```sh +git clone https://github.com/roc-lang/examples.git cd examples -roc dev helloWorld.roc +roc ./HelloWorld/main.roc ``` -[crates/cli/tests/benchmarks](https://github.com/roc-lang/roc/tree/main/crates/cli/tests/benchmarks) contains more examples. - ## Getting Involved The number of people involved in Roc's development has been steadily increasing