Merge branch 'main' of https://github.com/roc-lang/roc into list-splitting

This commit is contained in:
Isaac Van Doren 2024-11-15 21:12:51 -06:00
commit 35f221d9de
No known key found for this signature in database
GPG key ID: CFA524CD470E5B94
28 changed files with 295 additions and 370 deletions

View file

@ -1,7 +1,6 @@
on:
workflow_dispatch:
#pull_request:
# TODO remove pull_request trigger
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

View file

@ -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"

View file

@ -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: |

View file

@ -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

View file

@ -54,6 +54,6 @@ fi
./jump-start.sh
# build the basic cli platform
roc build.roc --prebuilt-platform
roc build.roc
cd ..

View file

@ -47,6 +47,6 @@ cd ..
cd basic-webserver
roc build.roc --prebuilt-platform
roc build.roc
cd ..

View file

@ -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

View file

@ -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,8 +517,11 @@ pub fn test(matches: &ArgMatches, target: Target) -> io::Result<i32> {
Some(n) => Threading::AtMost(*n),
};
let path = matches.get_one::<PathBuf>(ROC_FILE).unwrap();
let paths: Vec<_> = matches.get_many::<PathBuf>(ROC_FILE).unwrap().collect();
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();
@ -535,10 +539,20 @@ pub fn test(matches: &ArgMatches, target: Target) -> io::Result<i32> {
}
_ => 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);
} else if path.is_dir() {
find_all_roc_files(path, &mut flatten_paths);
} else {
flatten_paths.push(path.clone());
}
}
flatten_paths
};
let mut all_files_total_failed_count = 0;
let mut all_files_total_passed_count = 0;
for path in paths.iter() {
let arena = &arena;
let function_kind = FunctionKind::from_env();
@ -643,8 +657,23 @@ pub fn test(matches: &ArgMatches, target: Target) -> io::Result<i32> {
}
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}");
}
}
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<i32> {
// 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);
Ok((all_files_total_failed_count > 0) as i32)
}
} else {
let test_summary_str =
test_summary(total_failed_count, total_passed_count, total_duration);
println!("{test_summary_str}");
}
Ok((total_failed_count > 0) as i32)
fn find_all_roc_files(path: &PathBuf, flatten_paths: &mut Vec<PathBuf>) {
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());
}
_ => {}
}
}
}

View file

@ -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()
)
});

View file

@ -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;
}
}
};

View file

@ -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(_, _, _) => {

View file

@ -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),
}
}

View file

@ -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,
})

View file

@ -438,7 +438,6 @@ fn is_multiline_assigned_field_help<T: Formattable>(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<T>(
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);
}
}
}
}

View file

@ -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<T>(
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);
}
}
}

View file

@ -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<Tag> {
}),
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,
}
}

View file

@ -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<Msg<'a>>;
/// Add a task to the queue, and notify all the listeners.
fn enqueue_task<'a>(
injector: &Injector<BuildTask<'a>>,
listeners: &[Sender<WorkerMsg>],
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<WorkerMsg>],
worker_wakers: &[Sender<()>],
injector: &Injector<BuildTask<'a>>,
msg_tx: &crossbeam::channel::Sender<Msg<'a>>,
msg_rx: &crossbeam::channel::Receiver<Msg<'a>>,
@ -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,7 +2015,12 @@ 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| {
roc_worker::worker_task(
worker,
injector,
stealers,
worker_wakup_rx,
|task| {
run_task(
task,
worker_arena,
@ -2024,7 +2029,8 @@ fn load_multi_threaded<'a>(
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<BuildTask<'a>>,
worker_listeners: &'a [Sender<WorkerMsg>],
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<BuildTask<'a>>,
worker_listeners: &'a [Sender<WorkerMsg>],
worker_wakers: &[Sender<()>],
arena: &'a Bump,
) -> Result<State<'a>, 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)
}

View file

@ -539,7 +539,6 @@ pub enum Expr<'a> {
// Problems
MalformedIdent(&'a str, crate::ident::BadIdent),
MalformedClosure,
MalformedSuffixed(&'a Loc<Expr<'a>>),
// 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,
}
}
}

View file

@ -2180,7 +2180,6 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
| Expr::DbgStmt(_, _)
| Expr::LowLevelDbg(_, _, _)
| Expr::Return(_, _)
| Expr::MalformedClosure
| Expr::MalformedSuffixed(..)
| Expr::PrecedenceConflict { .. }
| Expr::EmptyRecordBuilder(_)
@ -2254,7 +2253,6 @@ fn assigned_expr_field_to_pattern_help<'a>(
arena.alloc(assigned_expr_field_to_pattern_help(arena, nested)?),
spaces,
),
AssignedField::Malformed(string) => Pattern::Malformed(string),
AssignedField::IgnoredValue(_, _, _) => return Err(()),
})
}

View file

@ -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),
}

View file

@ -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<str>, roc_parse::ident::BadIdent, Region),
MalformedTypeName(Box<str>, 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, _)

View file

@ -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,20 +23,11 @@ pub fn worker_task_step<Task>(
worker: &Worker<Task>,
injector: &Injector<Task>,
stealers: &[Stealer<Task>],
worker_msg_rx: &Receiver<WorkerMsg>,
worker_wakeup_rx: &Receiver<()>,
run_task: impl Fn(Task) -> Result<(), ChannelProblem>,
) -> Result<ControlFlow<(), ()>, 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 => {
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.
@ -58,12 +43,11 @@ pub fn worker_task_step<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,20 +57,11 @@ pub fn worker_task<Task>(
worker: Worker<Task>,
injector: &Injector<Task>,
stealers: &[Stealer<Task>],
worker_msg_rx: crossbeam::channel::Receiver<WorkerMsg>,
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 => {
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.
@ -100,8 +75,6 @@ pub fn worker_task<Task>(
run_task(task)?;
}
}
}
}
Ok(())
}
@ -110,17 +83,17 @@ pub fn start_tasks<State, Task, Tasks: IntoIterator<Item = Task>>(
state: &mut State,
work: MutSet<(ModuleId, Phase)>,
injector: &Injector<Task>,
worker_listeners: &[Sender<WorkerMsg>],
worker_wakers: &[Sender<()>],
mut start_phase: impl FnMut(ModuleId, Phase, &mut State) -> Tasks,
) -> Result<(), SendError<WorkerMsg>> {
) -> 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(())?;
}
}
}

View file

@ -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;

View file

@ -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<'_>> {
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;]

View file

@ -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 \

View file

@ -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 => "<function>",
Expr::Closure(_, _) => "<function>",
_ => {
let mut expr = roc_fmt::Buf::new_in(arena);

View file

@ -2194,9 +2194,6 @@ fn pretty_runtime_error<'b>(
title = SYNTAX_PROBLEM;
}
RuntimeError::MalformedClosure(_) => {
todo!("");
}
RuntimeError::MalformedSuffixed(_) => {
todo!("error for malformed suffix");
}

View file

@ -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