diff --git a/Cargo.lock b/Cargo.lock index b432181ebda..e0a118699a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2392,7 +2392,6 @@ dependencies = [ "bitvec", "bumpalo", "indoc", - "insta", "pretty_assertions", "roc_collections", "roc_error_macros", diff --git a/crates/cli/tests/benchmarks/platform/PlatformTasks.roc b/crates/cli/tests/benchmarks/platform/PlatformTasks.roc deleted file mode 100644 index 5db7e79c88b..00000000000 --- a/crates/cli/tests/benchmarks/platform/PlatformTasks.roc +++ /dev/null @@ -1,9 +0,0 @@ -hosted PlatformTasks - exposes [put_line, put_int, get_int] - imports [] - -put_line : Str -> Task {} * - -put_int : I64 -> Task {} * - -get_int : Task { value : I64, is_error : Bool } * diff --git a/crates/compiler/builtins/README.md b/crates/compiler/builtins/README.md index 4f8c1fe200a..bb8d21bfc36 100644 --- a/crates/compiler/builtins/README.md +++ b/crates/compiler/builtins/README.md @@ -16,14 +16,14 @@ You can run your new tests locally using `cargo test-gen-llvm`. You can add a fi ### module/src/symbol.rs -Towards the bottom of `symbol.rs` there is a `define_builtins!` macro being used that takes many modules and function names. The first level (`List`, `Int` ..) is the module name, and the second level is the function or value name (`reverse`, `rem` ..). If you wanted to add a `Int` function called `addTwo` go to `2 Int: "Int" => {` and inside that case add to the bottom `38 INT_ADD_TWO: "addTwo"` (assuming there are 37 existing ones). +Towards the bottom of `symbol.rs` there is a `define_builtins!` macro being used that takes many modules and function names. The first level (`List`, `Int` ..) is the module name, and the second level is the function or value name (`reverse`, `rem` ..). If you wanted to add a `Int` function called `add_two` go to `2 Int: "Int" => {` and inside that case add to the bottom `38 INT_ADD_TWO: "add_two"` (assuming there are 37 existing ones). Some of these have `#` inside their name (`first#list`, `#lt` ..). This is a trick we are doing to hide implementation details from Roc programmers. To a Roc programmer, a name with `#` in it is invalid, because `#` means everything after it is parsed to a comment. We are constructing these functions manually, so we are circumventing the parsing step and don't have such restrictions. We get to make functions and values with `#` which as a consequence are not accessible to Roc programmers. Roc programmers simply cannot reference them. But we can use these values and some of these are necessary for implementing builtins. For example, `List.get` returns tags, and it is not easy for us to create tags when composing LLVM. What is easier however, is: -- ..writing `List.#getUnsafe` that has the dangerous signature of `List elem, U64 -> elem` in LLVM -- ..writing `List elem, U64 -> Result elem [OutOfBounds]*` in a type safe way that uses `getUnsafe` internally, only after it checks if the `elem` at `U64` index exists. +- ..writing `List.#get_unsafe` that has the dangerous signature of `List elem, U64 -> elem` in LLVM +- ..writing `List elem, U64 -> Result elem [OutOfBounds]*` in a type safe way that uses `get_unsafe` internally, only after it checks if the `elem` at `U64` index exists. ### can/src/builtins.rs @@ -123,5 +123,5 @@ But replace `Num.atan`, the return value, and the return type with your new buil When implementing a new builtin, it is often easy to copy and paste the implementation for an existing builtin. This can take you quite far since many builtins are very similar, but it also risks forgetting to change one small part of what you copy and pasted and losing a lot of time later on when you cant figure out why things don't work. So, speaking from experience, even if you are copying an existing builtin, try and implement it manually without copying and pasting. Two recent instances of this (as of September 7th, 2020): -- `List.keepIf` did not work for a long time because in builtins its `LowLevel` was `ListMap`. This was because I copy and pasted the `List.map` implementation in `builtins.rs -- `List.walkBackwards` had mysterious memory bugs for a little while because in `unique.rs` its return type was `list_type(flex(b))` instead of `flex(b)` since it was copy and pasted from `List.keepIf`. +- `List.keep_if` did not work for a long time because in builtins its `LowLevel` was `ListMap`. This was because I copy and pasted the `List.map` implementation in `builtins.rs +- `List.walk_backwards` had mysterious memory bugs for a little while because in `unique.rs` its return type was `list_type(flex(b))` instead of `flex(b)` since it was copy and pasted from `List.keep_if`. diff --git a/crates/compiler/builtins/roc/Task.roc b/crates/compiler/builtins/roc/Task.roc deleted file mode 100644 index 6477cf92826..00000000000 --- a/crates/compiler/builtins/roc/Task.roc +++ /dev/null @@ -1,305 +0,0 @@ -module [ - Task, - ok, - err, - await, - map, - map_err, - on_err, - attempt, - forever, - loop, - from_result, - batch, - combine, - sequence, - for_each, - result, -] - -import List -import Result exposing [Result] - -## A Task represents an effect; an interaction with state outside your Roc -## program, such as the terminal's standard output, or a file. -Task ok err := {} -> Result ok err - -## Run a task repeatedly, until it fails with `err`. Note that this task does not return a success value. -forever : Task a err -> Task * err -forever = \@Task(task) -> - looper = \{} -> - when task({}) is - Err(e) -> Err(e) - Ok(_) -> looper({}) - - @Task(\{} -> looper({})) - -## Run a task repeatedly, until it fails with `err` or completes with `done`. -## -## ``` -## sum = -## Task.loop!(0, \total -> -## num_result = -## Stdin.line -## |> Task.result! -## |> Result.try(Str.toU64) -## -## when num_result is -## Ok num -> Task.ok(Step(total + num)) -## Err(StdinErr(EndOfFile)) -> Task.ok(Done(total)) -## Err(InvalidNumStr) -> Task.err(NonNumberGiven) -## ) -## ``` -loop : state, (state -> Task [Step state, Done done] err) -> Task done err -loop = \state, step -> - looper = \current -> - @Task(next) = step(current) - when next({}) is - Err(e) -> Err(e) - Ok(Done(new_result)) -> Ok(new_result) - Ok(Step(new_state)) -> looper(new_state) - - @Task(\{} -> looper(state)) - -## Create a task that always succeeds with the value provided. -## -## ``` -## # Always succeeds with "Louis" -## get_name : Task.Task Str * -## get_name = Task.ok("Louis") -## ``` -## -ok : a -> Task a * -ok = \a -> @Task(\{} -> Ok(a)) - -## Create a task that always fails with the error provided. -## -## ``` -## # Always fails with the tag `CustomError Str` -## customError : Str -> Task.Task {} [CustomError Str] -## customError = \err -> Task.err(CustomError(err)) -## ``` -## -err : a -> Task * a -err = \a -> @Task(\{} -> Err(a)) - -## Transform a given Task with a function that handles the success or error case -## and returns another task based on that. This is useful for chaining tasks -## together or performing error handling and recovery. -## -## Consider the following task: -## -## `can_fail : Task {} [Failure, AnotherFail, YetAnotherFail]` -## -## We can use [attempt] to handle the failure cases using the following: -## -## ``` -## Task.attempt(can_fail, \result -> -## when result is -## Ok(Success) -> Stdout.line("Success!") -## Err(Failure) -> Stdout.line("Oops, failed!") -## Err(AnotherFail) -> Stdout.line("Ooooops, another failure!") -## Err(YetAnotherFail) -> Stdout.line("Really big oooooops, yet again!") -## ) -## ``` -## -## Here we know that the `can_fail` task may fail, and so we use -## `Task.attempt` to convert the task to a `Result` and then use pattern -## matching to handle the success and possible failure cases. -attempt : Task a b, (Result a b -> Task c d) -> Task c d -attempt = \@Task(task), transform -> - @Task( - \{} -> - @Task(transformed) = transform(task({})) - - transformed({}), - ) - -## Take the success value from a given [Task] and use that to generate a new [Task]. -## -## We can [await] Task results with callbacks: -## -## ``` -## Task.await(Stdin.line("What's your name?")), \name -> -## Stdout.line("Your name is: $(name)") -## ) -## ``` -## -## Or we can more succinctly use the `!` bang operator, which desugars to [await]: -## -## ``` -## name = Stdin.line!("What's your name?") -## Stdout.line("Your name is: $(name)") -## ``` -await : Task a b, (a -> Task c b) -> Task c b -await = \@Task(task), transform -> - @Task( - \{} -> - when task({}) is - Ok(a) -> - @Task(transformed) = transform(a) - transformed({}) - - Err(b) -> - Err(b), - ) - -## Take the error value from a given [Task] and use that to generate a new [Task]. -## -## ``` -## # Prints "Something went wrong!" to standard error if `can_fail` fails. -## can_fail -## |> Task.on_err(\_ -> Stderr.line("Something went wrong!")) -## ``` -on_err : Task a b, (b -> Task a c) -> Task a c -on_err = \@Task(task), transform -> - @Task( - \{} -> - when task({}) is - Ok(a) -> - Ok(a) - - Err(b) -> - @Task(transformed) = transform(b) - transformed({}), - ) - -## Transform the success value of a given [Task] with a given function. -## -## ``` -## # Succeeds with a value of "Bonjour Louis!" -## Task.ok("Louis") -## |> Task.map(\name -> "Bonjour $(name)!") -## ``` -map : Task a c, (a -> b) -> Task b c -map = \@Task(task), transform -> - @Task( - \{} -> - when task({}) is - Ok(a) -> Ok(transform(a)) - Err(b) -> Err(b), - ) - -## Transform the error value of a given [Task] with a given function. -## -## ``` -## # Ignore the fail value, and map it to the tag `CustomError` -## can_fail -## |> Task.map_err(\_ -> CustomError) -## ``` -map_err : Task c a, (a -> b) -> Task c b -map_err = \@Task(task), transform -> - @Task( - \{} -> - when task({}) is - Ok(a) -> Ok(a) - Err(b) -> Err(transform(b)), - ) - -## Use a Result among other Tasks by converting it into a [Task]. -from_result : Result a b -> Task a b -from_result = \res -> - @Task(\{} -> res) - -## Apply a task to another task applicatively. -## -## DEPRECATED: Modern record builders use [combine]. -batch : Task a c -> (Task (a -> b) c -> Task b c) -batch = \current -> - \next -> - await( - next, - \f -> - map(current, f), - ) - -## Combine the values of two tasks with a custom combining function. -## -## This is primarily used with record builders. -## -## ``` -## { a, b, c } = -## { Task.combine <- -## a: Task.ok(123), -## b: File.read("file.txt"), -## c: Http.get("http://api.com/"), -## }! -## ``` -combine : Task a err, Task b err, (a, b -> c) -> Task c err -combine = \@Task(left_task), @Task(right_task), combiner -> - @Task( - \{} -> - left = try(left_task, {}) - right = try(right_task, {}) - - Ok(combiner(left, right)), - ) - -## Apply each task in a list sequentially, and return a list of the resulting values. -## Each task will be awaited before beginning the next task. -## -## ``` -## fetch_author_tasks : List (Task Author [DbError]) -## -## get_authors : Task (List Author) [DbError] -## get_authors = Task.sequence(fetch_author_tasks) -## ``` -## -sequence : List (Task ok err) -> Task (List ok) err -sequence = \task_list -> - Task.loop( - (task_list, List.with_capacity(List.len(task_list))), - \(tasks, values) -> - when tasks is - [task, .. as rest] -> - Task.map( - task, - \value -> - Step((rest, List.append(values, value))), - ) - - [] -> - Task.ok(Done(values)), - ) - -## Apply a task repeatedly for each item in a list -## -## ``` -## authors : List Author -## save_author : Author -> Task {} [DbError] -## -## save_authors : Task (List Author) [DbError] -## save_authors = Task.for_each(authors, save_author) -## ``` -## -for_each : List a, (a -> Task {} b) -> Task {} b -for_each = \items, fn -> - List.walk( - items, - ok({}), - \state, item -> - state |> await(\_ -> fn(item)), - ) - -## Transform a task that can either succeed with `ok`, or fail with `err`, into -## a task that succeeds with `Result ok err`. -## -## This is useful when chaining tasks using the `!` suffix. For example: -## -## ``` -## # Path.roc -## check_file : Str -> Task [Good, Bad] [IOError] -## -## # main.roc -## when check_file("/usr/local/bin/roc") |> Task.result! is -## Ok(Good) -> "..." -## Ok(Bad) -> "..." -## Err(IOError) -> "..." -## ``` -## -result : Task ok err -> Task (Result ok err) * -result = \@Task(task) -> - @Task( - \{} -> - Ok(task({})), - ) diff --git a/crates/compiler/builtins/roc/main.roc b/crates/compiler/builtins/roc/main.roc index 36edf4ed49b..e884058468d 100644 --- a/crates/compiler/builtins/roc/main.roc +++ b/crates/compiler/builtins/roc/main.roc @@ -11,5 +11,4 @@ package [ Hash, Box, Inspect, - Task, ] {} diff --git a/crates/compiler/builtins/src/roc.rs b/crates/compiler/builtins/src/roc.rs index 2093c8e4cb2..d69f73f9148 100644 --- a/crates/compiler/builtins/src/roc.rs +++ b/crates/compiler/builtins/src/roc.rs @@ -16,7 +16,6 @@ pub fn module_source(module_id: ModuleId) -> &'static str { ModuleId::DECODE => DECODE, ModuleId::HASH => HASH, ModuleId::INSPECT => INSPECT, - ModuleId::TASK => TASK, _ => internal_error!( "ModuleId {:?} is not part of the standard library", module_id @@ -36,4 +35,3 @@ const ENCODE: &str = include_str!("../roc/Encode.roc"); const DECODE: &str = include_str!("../roc/Decode.roc"); const HASH: &str = include_str!("../roc/Hash.roc"); const INSPECT: &str = include_str!("../roc/Inspect.roc"); -const TASK: &str = include_str!("../roc/Task.roc"); diff --git a/crates/compiler/can/Cargo.toml b/crates/compiler/can/Cargo.toml index 3a0fd8ada65..78b1805f652 100644 --- a/crates/compiler/can/Cargo.toml +++ b/crates/compiler/can/Cargo.toml @@ -25,6 +25,5 @@ soa.workspace = true [dev-dependencies] indoc.workspace = true -insta.workspace = true pretty_assertions.workspace = true test_compile.workspace = true diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index 63a33e42819..6cde3be2eba 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -10,7 +10,6 @@ use crate::annotation::IntroducedVariables; use crate::annotation::OwnedNamedOrAble; use crate::derive; use crate::env::Env; -use crate::env::FxMode; use crate::expr::canonicalize_record; use crate::expr::get_lookup_symbols; use crate::expr::AnnotatedMark; @@ -3265,13 +3264,7 @@ fn to_pending_value_def<'a>( )) } StmtAfterExpr => PendingValue::StmtAfterExpr, - Stmt(expr) => { - if env.fx_mode == FxMode::Task { - internal_error!("a Stmt was not desugared correctly, should have been converted to a Body(...) in desguar") - } - - PendingValue::Def(PendingValueDef::Stmt(expr)) - } + Stmt(expr) => PendingValue::Def(PendingValueDef::Stmt(expr)), } } diff --git a/crates/compiler/can/src/desugar.rs b/crates/compiler/can/src/desugar.rs index d002b8fc0a3..e3f8a5772b5 100644 --- a/crates/compiler/can/src/desugar.rs +++ b/crates/compiler/can/src/desugar.rs @@ -1,18 +1,16 @@ #![allow(clippy::manual_map)] -use crate::env::{Env, FxMode}; +use crate::env::Env; use crate::scope::Scope; -use crate::suffixed::{apply_try_function, unwrap_suffixed_expression, EUnwrapped}; use bumpalo::collections::Vec; -use bumpalo::Bump; use roc_error_macros::internal_error; use roc_module::called_via::BinOp::{DoubleQuestion, Pizza}; use roc_module::called_via::{BinOp, CalledVia}; use roc_module::ident::ModuleName; use roc_parse::ast::Expr::{self, *}; use roc_parse::ast::{ - is_expr_suffixed, AssignedField, Collection, Defs, ModuleImportParams, Pattern, ResultTryKind, - StrLiteral, StrSegment, TryTarget, TypeAnnotation, ValueDef, WhenBranch, + AssignedField, Collection, Defs, ModuleImportParams, Pattern, ResultTryKind, StrLiteral, + StrSegment, ValueDef, WhenBranch, }; use roc_problem::can::Problem; use roc_region::all::{Loc, Region}; @@ -95,10 +93,7 @@ fn new_op_call_expr<'a>( ), ); } - TrySuffix { - target: TryTarget::Result, - expr: fn_expr, - } => { + TrySuffix(fn_expr) => { let loc_fn = env.arena.alloc(Loc::at(right.region, **fn_expr)); let function = desugar_expr(env, scope, loc_fn); @@ -119,11 +114,7 @@ fn new_op_call_expr<'a>( } PncApply( &Loc { - value: - TrySuffix { - target: TryTarget::Result, - expr: fn_expr, - }, + value: TrySuffix(fn_expr), region: fn_region, }, loc_args, @@ -154,11 +145,7 @@ fn new_op_call_expr<'a>( } Apply( &Loc { - value: - TrySuffix { - target: TryTarget::Result, - expr: fn_expr, - }, + value: TrySuffix(fn_expr), region: fn_region, }, loc_args, @@ -386,46 +373,7 @@ fn desugar_value_def<'a>( "StmtAfterExpression is only created during desugaring, so it shouldn't exist here." ), - Stmt(stmt_expr) => { - if env.fx_mode == FxMode::PurityInference { - // In purity inference mode, statements aren't fully desugared here - // so we can provide better errors - return Stmt(desugar_expr(env, scope, stmt_expr)); - } - - // desugar `stmt_expr!` to - // _ : {} - // _ = stmt_expr! - - let desugared_expr = desugar_expr(env, scope, stmt_expr); - - if !is_expr_suffixed(&desugared_expr.value) - && !matches!(&desugared_expr.value, LowLevelTry(_, _)) - { - env.problems.push(Problem::StmtAfterExpr(stmt_expr.region)); - - return ValueDef::StmtAfterExpr; - } - - let region = stmt_expr.region; - let new_pat = env - .arena - .alloc(Loc::at(region, Pattern::Underscore("#!stmt"))); - - ValueDef::AnnotatedBody { - ann_pattern: new_pat, - ann_type: env.arena.alloc(Loc::at( - region, - TypeAnnotation::Record { - fields: Collection::empty(), - ext: None, - }, - )), - lines_between: &[], - body_pattern: new_pat, - body_expr: desugared_expr, - } - } + Stmt(stmt_expr) => Stmt(desugar_expr(env, scope, stmt_expr)), } } @@ -433,147 +381,10 @@ pub fn desugar_defs_node_values<'a>( env: &mut Env<'a>, scope: &mut Scope, defs: &mut roc_parse::ast::Defs<'a>, - top_level_def: bool, ) { for value_def in defs.value_defs.iter_mut() { *value_def = desugar_value_def(env, scope, env.arena.alloc(*value_def)); } - - // `desugar_defs_node_values` is called recursively in `desugar_expr` - // and we only want to unwrap suffixed nodes if they are a top level def. - // - // check here first so we only unwrap the expressions once, and after they have - // been desugared - if top_level_def { - for value_def in defs.value_defs.iter_mut() { - *value_def = desugar_value_def_suffixed(env.arena, *value_def); - } - } -} - -/// For each top-level ValueDef in our module, we will unwrap any suffixed -/// expressions -/// -/// e.g. `say! "hi"` desugars to `Task.await (say "hi") \{} -> ...` -pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>) -> ValueDef<'a> { - use ValueDef::*; - - match value_def { - Body(loc_pattern, loc_expr) => { - // note called_from_def is passed as `false` as this is a top_level_def - match unwrap_suffixed_expression(arena, loc_expr, None) { - Ok(new_expr) => Body(loc_pattern, new_expr), - Err(EUnwrapped::UnwrappedSubExpr { - sub_arg, - sub_pat, - sub_new, - target, - }) => desugar_value_def_suffixed( - arena, - Body( - loc_pattern, - apply_try_function( - arena, - loc_expr.region, - sub_arg, - sub_pat, - sub_new, - None, - target, - ), - ), - ), - Err(..) => Body( - loc_pattern, - arena.alloc(Loc::at(loc_expr.region, MalformedSuffixed(loc_expr))), - ), - } - } - ann @ Annotation(_, _) => ann, - AnnotatedBody { - ann_pattern, - ann_type, - lines_between, - body_pattern, - body_expr, - } => { - match unwrap_suffixed_expression(arena, body_expr, Some(ann_pattern)) { - Ok(new_expr) => AnnotatedBody { - ann_pattern, - ann_type, - lines_between, - body_pattern, - body_expr: new_expr, - }, - Err(EUnwrapped::UnwrappedSubExpr { - sub_arg, - sub_pat, - sub_new, - target, - }) => desugar_value_def_suffixed( - arena, - AnnotatedBody { - ann_pattern, - ann_type, - lines_between, - body_pattern, - body_expr: apply_try_function( - arena, - body_expr.region, - sub_arg, - sub_pat, - sub_new, - Some((ann_pattern, ann_type)), - target, - ), - }, - ), - // When the last expression is suffixed, it will try to unwrap the def, but because we - // have an annotated def we can simply ignore the try and return it as is without - // creating an intermediate identifier - Err(EUnwrapped::UnwrappedDefExpr { loc_expr, .. }) => desugar_value_def_suffixed( - arena, - AnnotatedBody { - ann_pattern, - ann_type, - lines_between, - body_pattern, - body_expr: loc_expr, - }, - ), - Err(..) => AnnotatedBody { - ann_pattern, - ann_type, - lines_between, - body_pattern, - body_expr: arena.alloc(Loc::at(body_expr.region, MalformedSuffixed(body_expr))), - }, - } - } - - Expect { - condition, - preceding_comment, - } => match unwrap_suffixed_expression(arena, condition, None) { - Ok(new_condition) => ValueDef::Expect { - condition: new_condition, - preceding_comment, - }, - Err(..) => { - internal_error!("Unable to desugar the suffix inside an Expect value def"); - } - }, - - // TODO support desugaring of Dbg - Dbg { .. } => value_def, - ModuleImport { .. } | IngestedFileImport(_) | StmtAfterExpr => value_def, - - Stmt(..) => { - internal_error!( - "this should have been desugared into a Body(..) before this call in desugar_expr" - ) - } - } } /// Reorder the expression tree based on operator precedence and associativity rules, @@ -588,10 +399,10 @@ pub fn desugar_expr<'a>( | Num(..) | NonBase10Int { .. } | SingleQuote(_) + | Var { .. } | AccessorFunction(_) | Underscore { .. } | MalformedIdent(_, _) - | MalformedSuffixed(..) | PrecedenceConflict { .. } | EmptyRecordBuilder(_) | SingleFieldRecordBuilder(_) @@ -601,23 +412,6 @@ pub fn desugar_expr<'a>( | Crash | Try => loc_expr, - Var { module_name, ident } => { - if env.fx_mode == FxMode::Task && ident.ends_with('!') { - env.arena.alloc(Loc::at( - Region::new(loc_expr.region.start(), loc_expr.region.end().sub(1)), - TrySuffix { - expr: env.arena.alloc(Var { - module_name, - ident: ident.trim_end_matches('!'), - }), - target: roc_parse::ast::TryTarget::Task, - }, - )) - } else { - loc_expr - } - } - Str(str_literal) => match str_literal { StrLiteral::PlainLine(_) => loc_expr, StrLiteral::Line(segments) => { @@ -651,32 +445,14 @@ pub fn desugar_expr<'a>( env.arena.alloc(Loc { region, value }) } - TrySuffix { - expr: sub_expr, - target, - } => { + TrySuffix(sub_expr) => { let intermediate = env.arena.alloc(Loc::at(loc_expr.region, **sub_expr)); let new_sub_loc_expr = desugar_expr(env, scope, intermediate); - match target { - TryTarget::Result => env.arena.alloc(Loc::at( - loc_expr.region, - LowLevelTry(new_sub_loc_expr, ResultTryKind::OperatorSuffix), - )), - TryTarget::Task => { - let new_sub_expr = env.arena.alloc(new_sub_loc_expr.value); - - // desugar the sub_expression, but leave the TrySuffix as this will - // be unwrapped later in desugar_value_def_suffixed - env.arena.alloc(Loc::at( - loc_expr.region, - TrySuffix { - expr: new_sub_expr, - target: *target, - }, - )) - } - } + env.arena.alloc(Loc::at( + loc_expr.region, + LowLevelTry(new_sub_loc_expr, ResultTryKind::OperatorSuffix), + )) } RecordAccess(sub_expr, paths) => { let region = loc_expr.region; @@ -1066,7 +842,7 @@ pub fn desugar_expr<'a>( BinOps(lefts, right) => desugar_bin_ops(env, scope, loc_expr.region, lefts, right), Defs(defs, loc_ret) => { let mut defs = (*defs).clone(); - desugar_defs_node_values(env, scope, &mut defs, false); + desugar_defs_node_values(env, scope, &mut defs); let loc_ret = desugar_expr(env, scope, loc_ret); env.arena.alloc(Loc::at( @@ -1132,11 +908,7 @@ pub fn desugar_expr<'a>( } Apply( Loc { - value: - TrySuffix { - target: TryTarget::Result, - expr: fn_expr, - }, + value: TrySuffix(fn_expr), region: fn_region, }, loc_args, diff --git a/crates/compiler/can/src/effect_module.rs b/crates/compiler/can/src/effect_module.rs index 1bea1c781c9..9b0758245e7 100644 --- a/crates/compiler/can/src/effect_module.rs +++ b/crates/compiler/can/src/effect_module.rs @@ -4,15 +4,18 @@ use crate::pattern::Pattern; use crate::scope::Scope; use roc_collections::SendMap; use roc_module::symbol::Symbol; +use roc_problem::can::{Problem, RuntimeError}; use roc_region::all::{Loc, Region}; use roc_types::subs::{VarStore, Variable}; -use roc_types::types::{LambdaSet, OptAbleVar, Type}; +use roc_types::types::Type; pub fn build_host_exposed_def( scope: &mut Scope, symbol: Symbol, + region: Region, ident: &str, var_store: &mut VarStore, + problems: &mut Vec, annotation: crate::annotation::Annotation, ) -> Def { let expr_var = var_store.fresh(); @@ -22,7 +25,6 @@ pub fn build_host_exposed_def( let mut arguments: Vec<(Variable, AnnotatedMark, Loc)> = Vec::new(); let mut linked_symbol_arguments: Vec<(Variable, Expr)> = Vec::new(); - let mut captured_symbols: Vec<(Symbol, Variable)> = Vec::new(); let crate::annotation::Annotation { introduced_variables, @@ -31,173 +33,57 @@ pub fn build_host_exposed_def( .. } = annotation; - let def_body = { - match typ.shallow_structural_dealias() { - Type::Function(args, _, _, fx) if **fx == Type::Pure => { - for i in 0..args.len() { - let name = format!("closure_arg_{ident}_{i}"); + let foreign_call = match typ.shallow_structural_dealias() { + Type::Function(args, _, _, _) => { + for i in 0..args.len() { + let name = format!("{ident}_arg_{i}"); - let arg_symbol = { - let ident = name.clone().into(); - scope.introduce(ident, Region::zero()).unwrap() - }; - - let arg_var = var_store.fresh(); - - arguments.push(( - arg_var, - AnnotatedMark::new(var_store), - Loc::at_zero(Pattern::Identifier(arg_symbol)), - )); - - captured_symbols.push((arg_symbol, arg_var)); - linked_symbol_arguments.push((arg_var, Expr::Var(arg_symbol, arg_var))); - } - - let foreign_symbol_name = format!("roc_fx_{ident}"); - let low_level_call = Expr::ForeignCall { - foreign_symbol: foreign_symbol_name.into(), - args: linked_symbol_arguments, - ret_var: var_store.fresh(), - }; - - let task_closure_symbol = { - let name = format!("task_closure_{ident}"); - - let ident = name.into(); + let arg_symbol = { + let ident = name.clone().into(); scope.introduce(ident, Region::zero()).unwrap() }; - let task_closure = Expr::Closure(ClosureData { - function_type: var_store.fresh(), - closure_type: var_store.fresh(), - return_type: var_store.fresh(), - fx_type: var_store.fresh(), - early_returns: vec![], - name: task_closure_symbol, - captured_symbols, - recursive: Recursive::NotRecursive, - arguments: vec![( - var_store.fresh(), - AnnotatedMark::new(var_store), - Loc::at_zero(empty_record_pattern(var_store)), - )], - loc_body: Box::new(Loc::at_zero(low_level_call)), - }); + let arg_var = var_store.fresh(); - let (specialized_def_type, type_arguments, lambda_set_variables) = - build_fresh_opaque_variables(var_store); - let body = Expr::OpaqueRef { - opaque_var: var_store.fresh(), - name: Symbol::TASK_TASK, - argument: Box::new((var_store.fresh(), Loc::at_zero(task_closure))), - specialized_def_type, - type_arguments, - lambda_set_variables, - }; + arguments.push(( + arg_var, + AnnotatedMark::new(var_store), + Loc::at_zero(Pattern::Identifier(arg_symbol)), + )); - Expr::Closure(ClosureData { - function_type: var_store.fresh(), - closure_type: var_store.fresh(), - return_type: var_store.fresh(), - fx_type: var_store.fresh(), - early_returns: vec![], - name: symbol, - captured_symbols: std::vec::Vec::new(), - recursive: Recursive::NotRecursive, - arguments, - loc_body: Box::new(Loc::at_zero(body)), - }) + linked_symbol_arguments.push((arg_var, Expr::Var(arg_symbol, arg_var))); } - Type::Function(args, _, _, fx) if **fx == Type::Effectful => { - for i in 0..args.len() { - let name = format!("{ident}_arg_{i}"); - - let arg_symbol = { - let ident = name.clone().into(); - scope.introduce(ident, Region::zero()).unwrap() - }; - let arg_var = var_store.fresh(); + let ident_without_bang = ident.trim_end_matches('!'); + let foreign_symbol_name = format!("roc_fx_{ident_without_bang}"); - arguments.push(( - arg_var, - AnnotatedMark::new(var_store), - Loc::at_zero(Pattern::Identifier(arg_symbol)), - )); - - linked_symbol_arguments.push((arg_var, Expr::Var(arg_symbol, arg_var))); - } - - let ident_without_bang = ident.trim_end_matches('!'); - let foreign_symbol_name = format!("roc_fx_{ident_without_bang}"); - let foreign_call = Expr::ForeignCall { - foreign_symbol: foreign_symbol_name.into(), - args: linked_symbol_arguments, - ret_var: var_store.fresh(), - }; - - Expr::Closure(ClosureData { - function_type: var_store.fresh(), - closure_type: var_store.fresh(), - return_type: var_store.fresh(), - fx_type: var_store.fresh(), - early_returns: vec![], - name: symbol, - captured_symbols: std::vec::Vec::new(), - recursive: Recursive::NotRecursive, - arguments, - loc_body: Box::new(Loc::at_zero(foreign_call)), - }) + Expr::ForeignCall { + foreign_symbol: foreign_symbol_name.into(), + args: linked_symbol_arguments, + ret_var: var_store.fresh(), } - _ => { - // not a function - - let foreign_symbol_name = format!("roc_fx_{ident}"); - let low_level_call = Expr::ForeignCall { - foreign_symbol: foreign_symbol_name.into(), - args: linked_symbol_arguments, - ret_var: var_store.fresh(), - }; - - let task_closure_symbol = { - let name = format!("task_closure_{ident}"); - - let ident = name.into(); - scope.introduce(ident, Region::zero()).unwrap() - }; - - let task_closure = Expr::Closure(ClosureData { - function_type: var_store.fresh(), - closure_type: var_store.fresh(), - return_type: var_store.fresh(), - fx_type: var_store.fresh(), - early_returns: vec![], - name: task_closure_symbol, - captured_symbols, - recursive: Recursive::NotRecursive, - arguments: vec![( - var_store.fresh(), - AnnotatedMark::new(var_store), - Loc::at_zero(empty_record_pattern(var_store)), - )], - loc_body: Box::new(Loc::at_zero(low_level_call)), - }); + } + _ => { + let runtime_error = RuntimeError::NonFunctionHostedAnnotation(region); + problems.push(Problem::RuntimeError(runtime_error.clone())); - let (specialized_def_type, type_arguments, lambda_set_variables) = - build_fresh_opaque_variables(var_store); - Expr::OpaqueRef { - opaque_var: var_store.fresh(), - name: Symbol::TASK_TASK, - argument: Box::new((var_store.fresh(), Loc::at_zero(task_closure))), - specialized_def_type, - type_arguments, - lambda_set_variables, - } - } + Expr::RuntimeError(runtime_error) } }; + let def_body = Expr::Closure(ClosureData { + function_type: var_store.fresh(), + closure_type: var_store.fresh(), + return_type: var_store.fresh(), + fx_type: var_store.fresh(), + early_returns: vec![], + name: symbol, + captured_symbols: std::vec::Vec::new(), + recursive: Recursive::NotRecursive, + arguments, + loc_body: Box::new(Loc::at_zero(foreign_call)), + }); + let def_annotation = crate::def::Annotation { signature: typ, introduced_variables, @@ -214,44 +100,3 @@ pub fn build_host_exposed_def( kind: DefKind::Let, } } - -fn build_fresh_opaque_variables( - var_store: &mut VarStore, -) -> (Box, Vec, Vec) { - let closure_var = var_store.fresh(); - - let ok_var = var_store.fresh(); - let err_var = var_store.fresh(); - let result_var = var_store.fresh(); - let fx_var = var_store.fresh(); - - let actual = Type::Function( - vec![Type::EmptyRec], - Box::new(Type::Variable(closure_var)), - Box::new(Type::Variable(result_var)), - Box::new(Type::Variable(fx_var)), - ); - - let type_arguments = vec![ - OptAbleVar { - var: ok_var, - opt_abilities: None, - }, - OptAbleVar { - var: err_var, - opt_abilities: None, - }, - ]; - let lambda_set_variables = vec![roc_types::types::LambdaSet(Type::Variable(closure_var))]; - - (Box::new(actual), type_arguments, lambda_set_variables) -} - -#[inline(always)] -fn empty_record_pattern(var_store: &mut VarStore) -> Pattern { - Pattern::RecordDestructure { - whole_var: var_store.fresh(), - ext_var: var_store.fresh(), - destructs: vec![], - } -} diff --git a/crates/compiler/can/src/env.rs b/crates/compiler/can/src/env.rs index 94805ed4d88..176fcefebcd 100644 --- a/crates/compiler/can/src/env.rs +++ b/crates/compiler/can/src/env.rs @@ -42,8 +42,6 @@ pub struct Env<'a> { pub opt_shorthand: Option<&'a str>, - pub fx_mode: FxMode, - pub src: &'a str, /// Lazily calculated line info. This data is only needed if the code contains calls to `dbg`, @@ -62,7 +60,6 @@ impl<'a> Env<'a> { dep_idents: &'a IdentIdsByModule, qualified_module_ids: &'a PackageModuleIds<'a>, opt_shorthand: Option<&'a str>, - fx_mode: FxMode, ) -> Env<'a> { Env { arena, @@ -79,7 +76,6 @@ impl<'a> Env<'a> { home_params_record: None, opt_shorthand, line_info: arena.alloc(None), - fx_mode, } } @@ -234,9 +230,3 @@ impl<'a> Env<'a> { self.line_info.as_ref().unwrap() } } - -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum FxMode { - PurityInference, - Task, -} diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index 0d21995d38e..8308f8a06bd 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -1548,10 +1548,6 @@ pub fn canonicalize_expr<'a>( (RuntimeError(problem), Output::default()) } - ast::Expr::MalformedSuffixed(..) => { - use roc_problem::can::RuntimeError::*; - (RuntimeError(MalformedSuffixed(region)), Output::default()) - } ast::Expr::EmptyRecordBuilder(sub_expr) => { use roc_problem::can::RuntimeError::*; @@ -2286,8 +2282,7 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool { ast::Expr::Tuple(fields) => fields .iter() .all(|loc_field| is_valid_interpolation(&loc_field.value)), - ast::Expr::MalformedSuffixed(loc_expr) - | ast::Expr::EmptyRecordBuilder(loc_expr) + ast::Expr::EmptyRecordBuilder(loc_expr) | ast::Expr::SingleFieldRecordBuilder(loc_expr) | ast::Expr::OptionalFieldInRecordBuilder(_, loc_expr) | ast::Expr::PrecedenceConflict(PrecedenceConflict { expr: loc_expr, .. }) @@ -2296,7 +2291,7 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool { ast::Expr::TupleAccess(sub_expr, _) | ast::Expr::ParensAround(sub_expr) | ast::Expr::RecordAccess(sub_expr, _) - | ast::Expr::TrySuffix { expr: sub_expr, .. } => is_valid_interpolation(sub_expr), + | ast::Expr::TrySuffix(sub_expr) => is_valid_interpolation(sub_expr), ast::Expr::Apply(loc_expr, args, _called_via) => { is_valid_interpolation(&loc_expr.value) && args diff --git a/crates/compiler/can/src/lib.rs b/crates/compiler/can/src/lib.rs index dd18a90ed90..dcc72e499a4 100644 --- a/crates/compiler/can/src/lib.rs +++ b/crates/compiler/can/src/lib.rs @@ -24,7 +24,6 @@ pub mod num; pub mod pattern; pub mod procedure; pub mod scope; -pub mod suffixed; pub mod traverse; pub use derive::DERIVED_REGION; diff --git a/crates/compiler/can/src/module.rs b/crates/compiler/can/src/module.rs index 17aa708727f..fd61d0682b5 100644 --- a/crates/compiler/can/src/module.rs +++ b/crates/compiler/can/src/module.rs @@ -4,7 +4,7 @@ use crate::abilities::{AbilitiesStore, ImplKey, PendingAbilitiesStore, ResolvedI use crate::annotation::{canonicalize_annotation, AnnotationFor}; use crate::def::{canonicalize_defs, report_unused_imports, Def, DefKind}; use crate::desugar::desugar_record_destructures; -use crate::env::{Env, FxMode}; +use crate::env::Env; use crate::expr::{ClosureData, Declarations, ExpectLookup, Expr, Output, PendingDerives}; use crate::pattern::{ canonicalize_record_destructs, BindingsFromPattern, Pattern, PermitShadows, RecordDestruct, @@ -224,7 +224,6 @@ pub fn canonicalize_module_defs<'a>( symbols_from_requires: &[(Loc, Loc>)], var_store: &mut VarStore, opt_shorthand: Option<&'a str>, - fx_mode: FxMode, ) -> ModuleOutput { let mut can_exposed_imports = MutMap::default(); @@ -246,7 +245,6 @@ pub fn canonicalize_module_defs<'a>( dep_idents, qualified_module_ids, opt_shorthand, - fx_mode, ); for (name, alias) in aliases.into_iter() { @@ -268,7 +266,7 @@ pub fn canonicalize_module_defs<'a>( // operators, and then again on *their* nested operators, ultimately applying the // rules multiple times unnecessarily. - crate::desugar::desugar_defs_node_values(&mut env, &mut scope, loc_defs, true); + crate::desugar::desugar_defs_node_values(&mut env, &mut scope, loc_defs); let mut rigid_variables = RigidVariables::default(); @@ -535,7 +533,13 @@ pub fn canonicalize_module_defs<'a>( }; let hosted_def = crate::effect_module::build_host_exposed_def( - &mut scope, *symbol, &ident, var_store, annotation, + &mut scope, + *symbol, + def_annotation.region, + &ident, + var_store, + &mut env.problems, + annotation, ); declarations.update_builtin_def(index, hosted_def); @@ -588,7 +592,13 @@ pub fn canonicalize_module_defs<'a>( }; let hosted_def = crate::effect_module::build_host_exposed_def( - &mut scope, *symbol, &ident, var_store, annotation, + &mut scope, + *symbol, + def_annotation.region, + &ident, + var_store, + &mut env.problems, + annotation, ); declarations.update_builtin_def(index, hosted_def); diff --git a/crates/compiler/can/src/suffixed.rs b/crates/compiler/can/src/suffixed.rs deleted file mode 100644 index 3b172206237..00000000000 --- a/crates/compiler/can/src/suffixed.rs +++ /dev/null @@ -1,1046 +0,0 @@ -#![allow(clippy::manual_map)] - -use bumpalo::collections::Vec; -use bumpalo::Bump; -use roc_error_macros::internal_error; -use roc_module::called_via::CalledVia; -use roc_module::ident::ModuleName; -use roc_parse::ast::Expr::{self, *}; -use roc_parse::ast::{is_expr_suffixed, Pattern, TryTarget, TypeAnnotation, ValueDef, WhenBranch}; -use roc_region::all::{Loc, Region}; -use std::cell::Cell; - -thread_local! { - // we use a thread_local here so that tests consistently give the same pattern - static SUFFIXED_ANSWER_COUNTER: Cell = const { Cell::new(0) }; -} - -/// Generates a unique identifier, useful for intermediate items during desugaring. -fn next_unique_suffixed_ident() -> String { - SUFFIXED_ANSWER_COUNTER.with(|counter| { - let count = counter.get(); - counter.set(count + 1); - - // # is used as prefix because it's impossible for code authors to define names like this. - // This makes it easy to see this identifier was created by the compiler. - format!("#!{}", count) - }) -} - -#[derive(Debug)] -/// -pub enum EUnwrapped<'a> { - /// First suffixed expression in a definition. Emitted if an expression has def pattern - /// e.g. x = first! (second! 42) - /// The first unwrap will produce - /// `UnwrappedDefExpr` - UnwrappedDefExpr { - loc_expr: &'a Loc>, - target: TryTarget, - }, - - /// Suffixed sub expression - /// e.g. x = first! (second! 42) - /// In this example, the second unwrap (after unwrapping the top level `first!`) will produce - /// `UnwrappedSubExpr<{ sub_arg: second 42, sub_pat: #!0_arg, sub_new: #!0_arg }>` - UnwrappedSubExpr { - /// the unwrapped expression argument for `try` functions - sub_arg: &'a Loc>, - - /// the pattern for the closure - sub_pat: &'a Loc>, - - /// the expression to replace the unwrapped - sub_new: &'a Loc>, - - /// The type of the target for the suffix, e.g. a Task or Result - target: TryTarget, - }, - - /// Malformed use of the suffix - Malformed, -} - -fn init_unwrapped_err<'a>( - arena: &'a Bump, - unwrapped_expr: &'a Loc>, - maybe_def_pat: Option<&'a Loc>>, - target: TryTarget, -) -> Result<&'a Loc>, EUnwrapped<'a>> { - match maybe_def_pat { - Some(..) => { - // we have a def pattern, so no need to generate a new pattern - // as this should only be created in the first call from a def - Err(EUnwrapped::UnwrappedDefExpr { - loc_expr: unwrapped_expr, - target, - }) - } - None => { - // Provide an intermediate answer expression and pattern when unwrapping a - // (sub) expression. - // e.g. `x = foo (bar!)` unwraps to `x = Task.await (bar) \#!0_arg -> foo #!0_arg` - let ident = arena.alloc(format!("{}_arg", next_unique_suffixed_ident())); - let sub_new = arena.alloc(Loc::at( - unwrapped_expr.region, - Expr::Var { - module_name: "", - ident, - }, - )); - let sub_pat = arena.alloc(Loc::at( - unwrapped_expr.region, - Pattern::Identifier { ident }, - )); - - Err(EUnwrapped::UnwrappedSubExpr { - sub_arg: unwrapped_expr, - sub_pat, - sub_new, - target, - }) - } - } -} - -/// Descend through the AST and unwrap each suffixed expression -/// when an expression is unwrapped, we apply the appropriate try function and -/// then descend through the AST again until there are no more suffixed -/// expressions, or we hit an error -pub fn unwrap_suffixed_expression<'a>( - arena: &'a Bump, - loc_expr: &'a Loc>, - maybe_def_pat: Option<&'a Loc>>, -) -> Result<&'a Loc>, EUnwrapped<'a>> { - let unwrapped_expression = { - match loc_expr.value { - Expr::TrySuffix { - expr: sub_expr, - target, - } => { - let unwrapped_sub_expr = arena.alloc(Loc::at(loc_expr.region, *sub_expr)); - - init_unwrapped_err(arena, unwrapped_sub_expr, maybe_def_pat, target) - } - - Expr::Defs(..) => unwrap_suffixed_expression_defs_help(arena, loc_expr, maybe_def_pat), - - Expr::Apply(..) => { - unwrap_suffixed_expression_apply_help(arena, loc_expr, maybe_def_pat) - } - - Expr::When(..) => unwrap_suffixed_expression_when_help(arena, loc_expr, maybe_def_pat), - - Expr::If { .. } => { - unwrap_suffixed_expression_if_then_else_help(arena, loc_expr, maybe_def_pat) - } - - Expr::Closure(..) => { - unwrap_suffixed_expression_closure_help(arena, loc_expr, maybe_def_pat) - } - - Expr::ParensAround(..) => { - unwrap_suffixed_expression_parens_help(arena, loc_expr, maybe_def_pat) - } - - Expr::SpaceBefore(..) | Expr::SpaceAfter(..) => { - internal_error!( - "SpaceBefore and SpaceAfter should have been removed in desugar_expr" - ); - } - - Expr::BinOps(..) => { - internal_error!("BinOps should have been desugared in desugar_expr"); - } - - Expr::LowLevelDbg(..) => unwrap_low_level_dbg(arena, loc_expr, maybe_def_pat), - - // we only need to unwrap some expressions, leave the rest as is - _ => Ok(loc_expr), - } - }; - - // KEEP THIS HERE FOR DEBUGGING - // USEFUL TO SEE THE UNWRAPPING - // OF AST NODES AS THEY DESCEND - // if is_expr_suffixed(&loc_expr.value) { - // eprintln!("{:?}, {:?}, {:?}", &maybe_def_pat, &loc_expr, &unwrapped_expression); - // } - - unwrapped_expression -} - -pub fn unwrap_suffixed_expression_parens_help<'a>( - arena: &'a Bump, - loc_expr: &'a Loc>, - _maybe_def_pat: Option<&'a Loc>>, -) -> Result<&'a Loc>, EUnwrapped<'a>> { - match loc_expr.value { - Expr::ParensAround(sub_loc_expr) => { - // note we use `None` here as we always want to generate a new pattern from child expressions - match unwrap_suffixed_expression(arena, arena.alloc(Loc::at_zero(*sub_loc_expr)), None) - { - Ok(new_expr) => { - let new_parens = arena.alloc(Loc::at( - loc_expr.region, - ParensAround(arena.alloc(new_expr.value)), - )); - Ok(new_parens) - } - Err(EUnwrapped::UnwrappedDefExpr { .. }) => { - internal_error!("unreachable, child expressions from ParensAround should generate UnwrappedSubExpr instead"); - } - Err(EUnwrapped::UnwrappedSubExpr { - sub_arg, - sub_pat, - sub_new, - target, - }) => { - let new_parens = arena.alloc(Loc::at( - loc_expr.region, - ParensAround(arena.alloc(sub_new.value)), - )); - Err(EUnwrapped::UnwrappedSubExpr { - sub_arg, - sub_pat, - sub_new: new_parens, - target, - }) - } - Err(err) => Err(err), - } - } - _ => internal_error!("unreachable, expected a ParensAround node to be passed into unwrap_suffixed_expression_parens_help"), - } -} - -pub fn unwrap_suffixed_expression_closure_help<'a>( - arena: &'a Bump, - loc_expr: &'a Loc>, - _maybe_def_pat: Option<&'a Loc>>, -) -> Result<&'a Loc>, EUnwrapped<'a>> { - match loc_expr.value { - Expr::Closure(closure_args, closure_loc_ret) => { - // note we use `None` here as we don't want to pass a DefExpr up and - // unwrap the definition pattern for the closure - match unwrap_suffixed_expression(arena, closure_loc_ret, None) { - Ok(unwrapped_expr) => { - let new_closure = arena.alloc(Loc::at(loc_expr.region, Expr::Closure(closure_args, unwrapped_expr))); - Ok(new_closure) - } - Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => { - let new_closure_loc_ret = apply_try_function(arena, loc_expr.region, sub_arg, sub_pat, sub_new, None, target); - let new_closure = arena.alloc(Loc::at(loc_expr.region, Expr::Closure(closure_args, new_closure_loc_ret))); - Ok(new_closure) - } - Err(err) => { - debug_assert!(false,"the closure Defs was malformed, got {:#?}", err); - Err(EUnwrapped::Malformed) - } - } - } - _ => internal_error!("unreachable, expected a Closure node to be passed into unwrap_suffixed_expression_closure_help"), - } -} - -pub fn unwrap_suffixed_expression_apply_help<'a>( - arena: &'a Bump, - loc_expr: &'a Loc>, - maybe_def_pat: Option<&'a Loc>>, -) -> Result<&'a Loc>, EUnwrapped<'a>> { - match loc_expr.value { - Expr::Apply(function, apply_args, called_via) => { - - // Any suffixed arguments will be innermost, therefore we unwrap those first - let local_args = arena.alloc_slice_copy(apply_args); - for arg in local_args.iter_mut() { - // Args are always expressions, don't pass `maybe_def_pat` - match unwrap_suffixed_expression(arena, arg, None) { - Ok(new_arg) => { - *arg = new_arg; - } - Err(EUnwrapped::UnwrappedDefExpr { .. }) => { - internal_error!("unreachable, unwrapped arg cannot be def expression as `None` was passed as pattern"); - } - Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new: new_arg, target }) => { - - *arg = new_arg; - - let new_apply = arena.alloc(Loc::at(loc_expr.region, Apply(function, local_args, called_via))); - return Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new: new_apply, target }); - } - Err(err) => return Err(err), - } - } - - // special case for when our Apply function is a suffixed Var (but not multiple suffixed) - if let Expr::TrySuffix { expr: sub_expr, target } = function.value { - let unwrapped_function = arena.alloc(Loc::at( - loc_expr.region, - *sub_expr, - )); - - let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(unwrapped_function, local_args, called_via))); - - match maybe_def_pat { - Some(..) => { - return Err(EUnwrapped::UnwrappedDefExpr { loc_expr: new_apply, target }); - } - None => { - return init_unwrapped_err(arena, new_apply, maybe_def_pat, target); - } - } - } - - // function is another expression - match unwrap_suffixed_expression(arena, function, maybe_def_pat) { - Ok(new_function) => { - let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(new_function, local_args, called_via))); - Ok(new_apply) - } - Err(EUnwrapped::UnwrappedDefExpr { loc_expr: unwrapped_function, target }) => { - let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(unwrapped_function, local_args, called_via))); - Err(EUnwrapped::UnwrappedDefExpr { loc_expr: new_apply, target }) - } - Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_function, sub_pat, sub_new, target }) => { - let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(sub_new, local_args, called_via))); - - Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_function, sub_pat, sub_new:new_apply, target }) - } - Err(err) => Err(err) - } - } - _ => internal_error!("unreachable, expected an Apply node to be passed into unwrap_suffixed_expression_apply_help"), - } -} - -/// Unwrap if-then-else statements -pub fn unwrap_suffixed_expression_if_then_else_help<'a>( - arena: &'a Bump, - loc_expr: &'a Loc>, - maybe_def_pat: Option<&'a Loc>>, -) -> Result<&'a Loc>, EUnwrapped<'a>> { - match loc_expr.value { - Expr::If { - if_thens, - final_else: final_else_branch, - indented_else, - } => { - for (index, if_then) in if_thens.iter().enumerate() { - let (current_if_then_statement, current_if_then_expression) = if_then; - - // unwrap suffixed (innermost) expressions e.g. `if true then doThing! then ...` - if is_expr_suffixed(¤t_if_then_expression.value) { - // split if_thens around the current index - let (before, after) = roc_parse::ast::split_around(if_thens, index); - - match unwrap_suffixed_expression(arena, current_if_then_expression, None) { - Ok(unwrapped_expression) => { - let mut new_if_thens = Vec::new_in(arena); - - new_if_thens.extend(before); - new_if_thens.push((*current_if_then_statement, *unwrapped_expression)); - new_if_thens.extend(after); - - let new_if = arena.alloc(Loc::at( - loc_expr.region, - Expr::If { - if_thens: arena.alloc_slice_copy(new_if_thens.as_slice()), - final_else: final_else_branch, - indented_else, - }, - )); - - return unwrap_suffixed_expression(arena, new_if, maybe_def_pat); - } - Err(EUnwrapped::UnwrappedDefExpr { .. }) => { - internal_error!("unexpected, unwrapped if-then-else Def expr should have intermediate answer as `None` was passed as pattern"); - } - Err(EUnwrapped::UnwrappedSubExpr { - sub_arg, - sub_pat, - sub_new, - target, - }) => { - let unwrapped_expression = apply_try_function( - arena, - sub_arg.region, - sub_arg, - sub_pat, - sub_new, - None, - target, - ); - - let mut new_if_thens = Vec::new_in(arena); - - new_if_thens.extend(before); - new_if_thens.push((*current_if_then_statement, *unwrapped_expression)); - new_if_thens.extend(after); - - let new_if = arena.alloc(Loc::at( - loc_expr.region, - Expr::If { - if_thens: arena.alloc_slice_copy(new_if_thens.as_slice()), - final_else: final_else_branch, - indented_else, - }, - )); - - return unwrap_suffixed_expression(arena, new_if, maybe_def_pat); - } - Err(EUnwrapped::Malformed) => return Err(EUnwrapped::Malformed), - } - } - - // unwrap suffixed statements e.g. `if isThing! then ...` - // note we want to split and nest if-then's so we only run Task's - // that are required - if is_expr_suffixed(¤t_if_then_statement.value) { - // split if_thens around the current index - let (before, after) = roc_parse::ast::split_around(if_thens, index); - - match unwrap_suffixed_expression(arena, current_if_then_statement, None) { - Ok(unwrapped_statement) => { - let mut new_if_thens = Vec::new_in(arena); - - new_if_thens.push((*unwrapped_statement, *current_if_then_expression)); - new_if_thens.extend(after); - - let new_if = arena.alloc(Loc::at( - loc_expr.region, - Expr::If { - if_thens: arena.alloc_slice_copy(new_if_thens.as_slice()), - final_else: final_else_branch, - indented_else, - }, - )); - - return unwrap_suffixed_expression(arena, new_if, maybe_def_pat); - } - Err(EUnwrapped::UnwrappedDefExpr { .. }) => { - internal_error!("unexpected, unwrapped if-then-else Def expr should have intermediate answer as `None` was passed as pattern"); - } - Err(EUnwrapped::UnwrappedSubExpr { - sub_arg, - sub_pat, - sub_new, - target, - }) => { - if before.is_empty() { - let mut new_if_thens = Vec::new_in(arena); - - new_if_thens.extend(before); - new_if_thens.push((*sub_new, *current_if_then_expression)); - new_if_thens.extend(after); - - let new_if = arena.alloc(Loc::at( - loc_expr.region, - Expr::If { - if_thens: arena.alloc_slice_copy(new_if_thens.as_slice()), - final_else: final_else_branch, - indented_else, - }, - )); - - let unwrapped_if_then = apply_try_function( - arena, - sub_arg.region, - sub_arg, - sub_pat, - new_if, - None, - target, - ); - - return unwrap_suffixed_expression( - arena, - unwrapped_if_then, - maybe_def_pat, - ); - } else { - let mut after_if_thens = Vec::new_in(arena); - - after_if_thens.push((*sub_new, *current_if_then_expression)); - after_if_thens.extend(after); - - let after_if = arena.alloc(Loc::at( - loc_expr.region, - Expr::If { - if_thens: arena.alloc_slice_copy(after_if_thens.as_slice()), - final_else: final_else_branch, - indented_else, - }, - )); - - let after_if_then = apply_try_function( - arena, - sub_arg.region, - sub_arg, - sub_pat, - after_if, - None, - target, - ); - - let before_if_then = arena.alloc(Loc::at( - loc_expr.region, - Expr::If { - if_thens: before, - final_else: after_if_then, - indented_else: false, - }, - )); - - return unwrap_suffixed_expression( - arena, - before_if_then, - maybe_def_pat, - ); - } - } - Err(EUnwrapped::Malformed) => return Err(EUnwrapped::Malformed), - } - } - } - - // check the final_else_branch - match unwrap_suffixed_expression(arena, final_else_branch, None) { - Ok(unwrapped_final_else) => { - return Ok(arena.alloc(Loc::at( - loc_expr.region, - Expr::If { - if_thens, - final_else: unwrapped_final_else, - indented_else, - }, - ))); - } - Err(EUnwrapped::UnwrappedDefExpr { .. }) => { - internal_error!("unexpected, unwrapped if-then-else Def expr should have intermediate answer as `None` was passed as pattern"); - } - Err(EUnwrapped::UnwrappedSubExpr { - sub_arg, - sub_pat, - sub_new, - target, - }) => { - let unwrapped_final_else = apply_try_function( - arena, - sub_arg.region, - sub_arg, - sub_pat, - sub_new, - None, - target, - ); - - let new_if = arena.alloc(Loc::at( - loc_expr.region, - Expr::If { - if_thens, - final_else: unwrapped_final_else, - indented_else, - }, - )); - - return unwrap_suffixed_expression(arena, new_if, maybe_def_pat); - } - Err(EUnwrapped::Malformed) => Err(EUnwrapped::Malformed), - } - } - _ => internal_error!("unreachable, expected an If expression to desugar"), - } -} - -pub fn unwrap_suffixed_expression_when_help<'a>( - arena: &'a Bump, - loc_expr: &'a Loc>, - maybe_def_pat: Option<&'a Loc>>, -) -> Result<&'a Loc>, EUnwrapped<'a>> { - match loc_expr.value { - Expr::When(condition, branches) => { - - // first unwrap any when branches values - // e.g. - // when foo is - // [] -> line! "bar" - // _ -> line! "baz" - for (branch_index, WhenBranch{value: branch_loc_expr,patterns, guard}) in branches.iter().enumerate() { - - // if the branch isn't suffixed we can leave it alone - if is_expr_suffixed(&branch_loc_expr.value) { - let unwrapped_branch_value = match unwrap_suffixed_expression(arena, branch_loc_expr, None) { - Ok(unwrapped_branch_value) => unwrapped_branch_value, - Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => apply_try_function(arena, branch_loc_expr.region, sub_arg, sub_pat, sub_new, None, target), - Err(..) => return Err(EUnwrapped::Malformed), - }; - - // TODO: unwrap guard - - let new_branch = WhenBranch{value: *unwrapped_branch_value, patterns, guard: *guard}; - let mut new_branches = Vec::new_in(arena); - let (before, rest) = branches.split_at(branch_index); - let after = &rest[1..]; - - new_branches.extend_from_slice(before); - new_branches.push(arena.alloc(new_branch)); - new_branches.extend_from_slice(after); - - let new_when = arena.alloc(Loc::at(loc_expr.region, Expr::When(condition, arena.alloc_slice_copy(new_branches.as_slice())))); - - return unwrap_suffixed_expression(arena, new_when, maybe_def_pat); - } - } - - // then unwrap the when condition - match unwrap_suffixed_expression(arena, condition, None) { - Ok(unwrapped_condition) => { - let new_when = arena.alloc(Loc::at(loc_expr.region, Expr::When(unwrapped_condition, branches))); - Ok(new_when) - } - Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => { - let new_when = arena.alloc(Loc::at(loc_expr.region, Expr::When(sub_new, branches))); - let applied_task_await = apply_try_function(arena,loc_expr.region,sub_arg,sub_pat,new_when, None, target); - Ok(applied_task_await) - } - Err(EUnwrapped::UnwrappedDefExpr { .. }) - | Err(EUnwrapped::Malformed) => Err(EUnwrapped::Malformed) - } - - } - _ => internal_error!("unreachable, expected a When node to be passed into unwrap_suffixed_expression_defs_help"), - } -} - -pub fn unwrap_suffixed_expression_defs_help<'a>( - arena: &'a Bump, - loc_expr: &'a Loc>, - maybe_def_pat: Option<&'a Loc>>, -) -> Result<&'a Loc>, EUnwrapped<'a>> { - match loc_expr.value { - Expr::Defs(defs, loc_ret) => { - - let mut local_defs = defs.clone(); - let tags = local_defs.tags.clone(); - - // try an unwrap each def, if none can be unwrapped, then try to unwrap the loc_ret - for (tag_index, type_or_value_def_index) in tags.iter().enumerate() { - use ValueDef::*; - - let mut current_value_def = match type_or_value_def_index.split() { - Ok(..) => { - // ignore type definitions - continue; - }, - Err(value_index) => *local_defs.value_defs.get(value_index.index()).unwrap(), - }; - - let maybe_suffixed_value_def = match current_value_def { - Annotation(..) | Dbg{..} | Expect{..} | Stmt(..) | ModuleImport{..} | IngestedFileImport(_) => None, - AnnotatedBody { body_pattern, body_expr, ann_type, ann_pattern, .. } => Some((body_pattern, body_expr, Some((ann_pattern, ann_type)))), - Body (def_pattern, def_expr) => Some((def_pattern, def_expr, None)), - StmtAfterExpr => None, - }; - - match maybe_suffixed_value_def { - None => { - // We can't unwrap this def type, continue - }, - Some((def_pattern, def_expr, ann_type)) => { - match unwrap_suffixed_expression(arena, def_expr, Some(def_pattern)) { - Ok(unwrapped_def) => { - current_value_def.replace_expr(unwrapped_def); - local_defs.replace_with_value_def(tag_index, current_value_def, def_expr.region); - } - Err(EUnwrapped::UnwrappedDefExpr { loc_expr: unwrapped_expr, target }) => { - let split_defs = local_defs.split_defs_around(tag_index); - let before_empty = split_defs.before.is_empty(); - let after_empty = split_defs.after.is_empty(); - if before_empty && after_empty { - // NIL before, NIL after -> SINGLE DEF - // We pass None as a def pattern here because it's desugaring of the ret expression - let next_expr = match unwrap_suffixed_expression(arena,loc_ret, None) { - Ok(next_expr) => next_expr, - Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => { - // We need to apply Task.ok here as the defs final expression was unwrapped - apply_try_function(arena,def_expr.region,sub_arg,sub_pat,sub_new, None, target) - } - Err(EUnwrapped::UnwrappedDefExpr { .. }) | Err(EUnwrapped::Malformed) => { - // TODO handle case when we have maybe_def_pat so can return an unwrapped up - return Err(EUnwrapped::Malformed); - }, - }; - return unwrap_suffixed_expression( - arena, - apply_try_function( - arena, - def_expr.region, - unwrapped_expr, - def_pattern, - next_expr, - ann_type, - target, - ), - maybe_def_pat - ); - } else if before_empty { - // NIL before, SOME after -> FIRST DEF - let new_defs = arena.alloc(Loc::at(def_expr.region, Defs(arena.alloc(split_defs.after), loc_ret))); - - let next_expr = match unwrap_suffixed_expression(arena,new_defs,maybe_def_pat){ - Ok(next_expr) => next_expr, - Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => { - apply_try_function(arena, def_expr.region, sub_arg, sub_pat, sub_new, None, target) - } - Err(EUnwrapped::UnwrappedDefExpr { .. }) | Err(EUnwrapped::Malformed) => { - // TODO handle case when we have maybe_def_pat so can return an unwrapped up - return Err(EUnwrapped::Malformed); - }, - }; - - return unwrap_suffixed_expression(arena, apply_try_function(arena,def_expr.region,unwrapped_expr,def_pattern,next_expr, ann_type, target), maybe_def_pat); - } else if after_empty { - // SOME before, NIL after -> LAST DEF - // We pass None as a def pattern here because it's desugaring of the ret expression - match unwrap_suffixed_expression(arena,loc_ret,None){ - Ok(new_loc_ret) => { - let applied_task_await = apply_try_function(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type, target); - let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_task_await))); - return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat); - }, - Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => { - let new_loc_ret = apply_try_function(arena,def_expr.region,sub_arg,sub_pat,sub_new, None, target); - let applied_task_await = apply_try_function(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type, target); - let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_task_await))); - return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat); - } - Err(EUnwrapped::UnwrappedDefExpr { .. }) => { - // TODO confirm this is correct with test case - return Err(EUnwrapped::Malformed); - } - Err(EUnwrapped::Malformed) => { - return Err(EUnwrapped::Malformed); - }, - } - } else { - // SOME before, SOME after -> MIDDLE DEF - let after_defs = arena.alloc(Loc::at(def_expr.region, Defs(arena.alloc(split_defs.after), loc_ret))); - - match unwrap_suffixed_expression(arena,after_defs,maybe_def_pat){ - Ok(new_loc_ret) => { - let applied_await = apply_try_function(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type, target); - let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_await))); - return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat); - }, - Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => { - let new_loc_ret = apply_try_function(arena, def_expr.region, sub_arg, sub_pat, sub_new, None, target); - let applied_await = apply_try_function(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type, target); - let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_await))); - return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat); - } - Err(EUnwrapped::UnwrappedDefExpr { .. }) | Err(EUnwrapped::Malformed) => { - // TODO handle case when we have maybe_def_pat so can return an unwrapped up - return Err(EUnwrapped::Malformed); - }, - }; - } - } - Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => { - let new_body_def = ValueDef::Body(def_pattern, sub_new); - local_defs.replace_with_value_def(tag_index,new_body_def, sub_new.region); - let new_defs_expr = arena.alloc(Loc::at(def_expr.region,Defs(arena.alloc(local_defs), loc_ret))); - let replaced_def = apply_try_function(arena,def_expr.region,sub_arg,sub_pat,new_defs_expr, ann_type, target); - return unwrap_suffixed_expression(arena,replaced_def,maybe_def_pat); - } - Err(err) => return Err(err) - } - } - } - } - - // try to unwrap the loc_ret - match unwrap_suffixed_expression(arena,loc_ret,None){ - Ok(new_loc_ret) => { - Ok(arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(local_defs), new_loc_ret)))) - }, - Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => { - let new_loc_ret = apply_try_function(arena, loc_expr.region,sub_arg,sub_pat,sub_new, None, target); - let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(local_defs), new_loc_ret))); - unwrap_suffixed_expression(arena, new_defs, None) - } - Err(EUnwrapped::UnwrappedDefExpr { .. }) => { - // TODO confirm this is correct with test case - Err(EUnwrapped::Malformed) - } - Err(EUnwrapped::Malformed) => { - Err(EUnwrapped::Malformed) - }, - } - }, - _ => internal_error!("unreachable, expected a Defs node to be passed into unwrap_suffixed_expression_defs_help"), - } -} - -fn unwrap_low_level_dbg<'a>( - arena: &'a Bump, - loc_expr: &'a Loc>, - maybe_def_pat: Option<&'a Loc>>, -) -> Result<&'a Loc>, EUnwrapped<'a>> { - match loc_expr.value { - LowLevelDbg(dbg_src, arg, rest) => { - if is_expr_suffixed(&arg.value) { - return match unwrap_suffixed_expression(arena, arg, maybe_def_pat) { - Ok(unwrapped_expr) => { - let new_dbg = arena.alloc(Loc::at( - loc_expr.region, - LowLevelDbg(dbg_src, unwrapped_expr, rest), - )); - - unwrap_low_level_dbg(arena, new_dbg, maybe_def_pat) - } - Err(EUnwrapped::UnwrappedSubExpr { - sub_arg, - sub_pat, - sub_new, - target, - }) => { - let new_dbg = arena.alloc(Loc::at( - loc_expr.region, - LowLevelDbg(dbg_src, sub_new, rest), - )); - - unwrap_suffixed_expression( - arena, - apply_try_function( - arena, - new_dbg.region, - sub_arg, - sub_pat, - new_dbg, - None, - target, - ), - maybe_def_pat, - ) - } - Err(EUnwrapped::UnwrappedDefExpr { .. }) => { - internal_error!( - "unreachable, arg of LowLevelDbg should generate UnwrappedSubExpr instead" - ); - } - Err(EUnwrapped::Malformed) => Err(EUnwrapped::Malformed), - }; - } - - match unwrap_suffixed_expression(arena, rest, maybe_def_pat) { - Ok(unwrapped_expr) => { - let new_dbg = arena.alloc(Loc::at( - loc_expr.region, - LowLevelDbg(dbg_src, arg, unwrapped_expr), - )); - Ok(&*new_dbg) - } - Err(EUnwrapped::UnwrappedDefExpr { - loc_expr: unwrapped_expr, - target, - }) => { - let new_dbg = arena.alloc(Loc::at( - loc_expr.region, - LowLevelDbg(dbg_src, arg, unwrapped_expr), - )); - Err(EUnwrapped::UnwrappedDefExpr { - loc_expr: new_dbg, - target, - }) - } - Err(EUnwrapped::UnwrappedSubExpr { - sub_arg: unwrapped_expr, - sub_pat, - sub_new, - target, - }) => { - let new_dbg = arena.alloc(Loc::at( - loc_expr.region, - LowLevelDbg(dbg_src, arg, unwrapped_expr), - )); - Err(EUnwrapped::UnwrappedSubExpr { - sub_arg: new_dbg, - sub_pat, - sub_new, - target, - }) - } - Err(EUnwrapped::Malformed) => Err(EUnwrapped::Malformed), - } - } - _ => internal_error!( - "unreachable, expected a LowLevelDbg node to be passed into unwrap_low_level_dbg" - ), - } -} - -/// Helper for try_function loc_expr \loc_pat -> loc_cont` -pub fn apply_try_function<'a>( - arena: &'a Bump, - region: Region, - loc_expr: &'a Loc>, - loc_pat: &'a Loc>, - loc_cont: &'a Loc>, - maybe_loc_ann: Option<(&'a Loc, &'a Loc>)>, - target: TryTarget, -) -> &'a Loc> { - let try_function_first_arg = match maybe_loc_ann { - Some((loc_ann_pat, loc_type)) => { - // loc_ann_pat : loc_type - // loc_pat = loc_expr! - // loc_cont - - // desugar to - // try_function - // ( - // #!0_expr : Target loc_type _ - // #!0_expr = loc_expr - // #!0_expr - // ) - // \loc_pat -> loc_cont - use roc_parse::ast::*; - - // #!0_expr or #!0_stmt - let new_ident = next_unique_suffixed_ident(); - let new_ident = match loc_pat.value { - Pattern::Underscore("#!stmt") => format!("{}_stmt", new_ident), - Pattern::Identifier { ident } - if ident.starts_with('#') && ident.ends_with("_stmt") => - { - format!("{}_stmt", new_ident) - } - _ => format!("{}_expr", new_ident), - }; - let new_ident = arena.alloc(new_ident); - - // #!0_expr (pattern) - // #!0_expr : Target loc_type _ - // #!0_expr = loc_expr - let target_type_name = match target { - TryTarget::Task => "Task", - TryTarget::Result => "Result", - }; - let value_def = ValueDef::AnnotatedBody { - ann_pattern: arena.alloc(Loc::at( - loc_ann_pat.region, - Pattern::Identifier { - ident: if loc_ann_pat.value.equivalent(&loc_pat.value) { - new_ident - } else { - // create another pattern to preserve inconsistency - arena.alloc(next_unique_suffixed_ident()) - }, - }, - )), - ann_type: arena.alloc(Loc::at( - loc_type.region, - TypeAnnotation::Apply( - arena.alloc(""), - arena.alloc(target_type_name), - arena.alloc([ - *loc_type, - Loc::at(loc_type.region, TypeAnnotation::Inferred), - ]), - ), - )), - lines_between: &[], - body_pattern: arena.alloc(Loc::at( - loc_pat.region, - Pattern::Identifier { ident: new_ident }, - )), - body_expr: loc_expr, - }; - - // #!0_expr (variable) - let new_var = arena.alloc(Loc::at( - region, - Expr::Var { - module_name: "", - ident: new_ident, - }, - )); - - // ( - // #!0_expr : Target loc_type _ - // #!0_expr = loc_expr - // #!0_expr - // ) - let mut defs = roc_parse::ast::Defs::default(); - defs.push_value_def(value_def, region, &[], &[]); - - arena.alloc(Loc::at( - Region::span_across(&loc_ann_pat.region, &loc_expr.region), - Defs(arena.alloc(defs), new_var), - )) - } - None => { - // loc_pat = loc_expr! - // loc_cont - - // desugar to - // try_function loc_expr \loc_pat -> loc_cont - loc_expr - } - }; - - // If the last expression is suffixed - don't await - // e.g. - // \x -> x! - // \x -> x - if is_matching_intermediate_answer(loc_pat, loc_cont) { - return try_function_first_arg; - } - - // \loc_pat -> loc_cont - let closure = arena.alloc(Loc::at(region, Closure(arena.alloc([*loc_pat]), loc_cont))); - - // try_function first_arg closure - let (try_function_module, try_function_ident, called_via) = match target { - TryTarget::Task => (ModuleName::TASK, "await", CalledVia::BangSuffix), - TryTarget::Result => (ModuleName::RESULT, "try", CalledVia::QuestionSuffix), - }; - arena.alloc(Loc::at( - region, - Apply( - arena.alloc(Loc { - region, - value: Var { - module_name: try_function_module, - ident: try_function_ident, - }, - }), - arena.alloc([try_function_first_arg, closure]), - called_via, - ), - )) -} - -pub fn is_matching_intermediate_answer<'a>( - loc_pat: &'a Loc>, - loc_new: &'a Loc>, -) -> bool { - let pat_ident = match loc_pat.value { - Pattern::Identifier { ident, .. } => Some(ident), - _ => None, - }; - - let exp_ident = match loc_new.value { - Expr::Var { - module_name: "", - ident, - .. - } => Some(ident), - _ => None, - }; - - match (pat_ident, exp_ident) { - (Some(a), Some(b)) => a == b, - _ => false, - } -} diff --git a/crates/compiler/can/tests/helpers/mod.rs b/crates/compiler/can/tests/helpers/mod.rs index 3b638f47178..2cf41f4c034 100644 --- a/crates/compiler/can/tests/helpers/mod.rs +++ b/crates/compiler/can/tests/helpers/mod.rs @@ -56,7 +56,6 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut &dep_idents, &qualified_module_ids, None, - roc_can::env::FxMode::PurityInference, ); // Desugar operators (convert them to Apply calls, taking into account diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_multiple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_multiple.snap deleted file mode 100644 index 6ba9b8e9ce2..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_multiple.snap +++ /dev/null @@ -1,116 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-28, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @15-22 Apply( - @15-22 Var { - module_name: "Task", - ident: "await", - }, - [ - @17-18 Var { - module_name: "", - ident: "a", - }, - @15-22 Closure( - [ - @17-18 Identifier { - ident: "#!0_arg", - }, - ], - @15-22 Apply( - @15-22 Var { - module_name: "Task", - ident: "await", - }, - [ - @20-21 Var { - module_name: "", - ident: "x", - }, - @15-22 Closure( - [ - @20-21 Identifier { - ident: "#!1_arg", - }, - ], - @15-22 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @15-22, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @11-12 Identifier { - ident: "c", - }, - @15-22 Apply( - @15-16 Var { - module_name: "", - ident: "b", - }, - [ - @17-18 Var { - module_name: "", - ident: "#!0_arg", - }, - @20-21 Var { - module_name: "", - ident: "#!1_arg", - }, - ], - Space, - ), - ), - ], - }, - @27-28 Var { - module_name: "", - ident: "c", - }, - ), - ), - ], - BangSuffix, - ), - ), - ], - BangSuffix, - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_single.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_single.snap deleted file mode 100644 index 8349f24751d..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_single.snap +++ /dev/null @@ -1,92 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-25, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @15-19 Apply( - @15-19 Var { - module_name: "Task", - ident: "await", - }, - [ - @17-18 Var { - module_name: "", - ident: "a", - }, - @15-19 Closure( - [ - @17-18 Identifier { - ident: "#!0_arg", - }, - ], - @15-19 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @15-19, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @11-12 Identifier { - ident: "c", - }, - @15-19 Apply( - @15-16 Var { - module_name: "", - ident: "b", - }, - [ - @17-18 Var { - module_name: "", - ident: "#!0_arg", - }, - ], - Space, - ), - ), - ], - }, - @24-25 Var { - module_name: "", - ident: "c", - }, - ), - ), - ], - BangSuffix, - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_suffixed.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_suffixed.snap deleted file mode 100644 index d9db4e765a8..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_suffixed.snap +++ /dev/null @@ -1,113 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-43, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @15-33 Apply( - @15-33 Var { - module_name: "Task", - ident: "await", - }, - [ - Apply( - Var { - module_name: "", - ident: "foo", - }, - [ - @25-32 Str( - PlainLine( - "hello", - ), - ), - ], - Space, - ), - @15-33 Closure( - [ - Identifier { - ident: "#!0_arg", - }, - ], - @15-33 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @15-33, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @11-12 Identifier { - ident: "x", - }, - @15-33 Apply( - @15-18 Var { - module_name: "", - ident: "bar", - }, - [ - @20-32 ParensAround( - Var { - module_name: "", - ident: "#!0_arg", - }, - ), - ], - Space, - ), - ), - ], - }, - @38-43 Apply( - @38-41 Var { - module_name: "", - ident: "baz", - }, - [ - @42-43 Var { - module_name: "", - ident: "x", - }, - ], - Space, - ), - ), - ), - ], - BangSuffix, - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_function_suffixed.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_function_suffixed.snap deleted file mode 100644 index ac8e3c7f0bc..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_function_suffixed.snap +++ /dev/null @@ -1,114 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-45, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @15-35 Apply( - @15-35 Var { - module_name: "Task", - ident: "await", - }, - [ - Apply( - Var { - module_name: "", - ident: "foo", - }, - [ - @21-26 Str( - PlainLine( - "bar", - ), - ), - ], - Space, - ), - @15-35 Closure( - [ - Identifier { - ident: "#!0_arg", - }, - ], - @15-35 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @15-35, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @11-12 Identifier { - ident: "x", - }, - @15-35 Apply( - @16-26 ParensAround( - Var { - module_name: "", - ident: "#!0_arg", - }, - ), - [ - @28-35 Str( - PlainLine( - "hello", - ), - ), - ], - Space, - ), - ), - ], - }, - @40-45 Apply( - @40-43 Var { - module_name: "", - ident: "baz", - }, - [ - @44-45 Var { - module_name: "", - ident: "x", - }, - ], - Space, - ), - ), - ), - ], - BangSuffix, - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__bang_in_pipe_root.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__bang_in_pipe_root.snap deleted file mode 100644 index b70a801863c..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__bang_in_pipe_root.snap +++ /dev/null @@ -1,94 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-28, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @15-22 Apply( - @15-22 Var { - module_name: "Task", - ident: "await", - }, - [ - @15-16 Var { - module_name: "", - ident: "a", - }, - @15-22 Closure( - [ - @15-16 Identifier { - ident: "#!0_arg", - }, - ], - @15-22 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @15-22, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @11-12 Identifier { - ident: "c", - }, - @15-22 Apply( - @21-22 Var { - module_name: "", - ident: "b", - }, - [ - @15-16 Var { - module_name: "", - ident: "#!0_arg", - }, - ], - BinOp( - Pizza, - ), - ), - ), - ], - }, - @27-28 Var { - module_name: "", - ident: "c", - }, - ), - ), - ], - BangSuffix, - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap deleted file mode 100644 index e43dd481ffb..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap +++ /dev/null @@ -1,106 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-26, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @11-14 Apply( - @11-14 Var { - module_name: "Task", - ident: "await", - }, - [ - @11-14 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @11-14, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @11-15 Identifier { - ident: "#!0_stmt", - }, - ann_type: @11-15 Apply( - "", - "Task", - [ - @11-15 Record { - fields: [], - ext: None, - }, - @11-15 Inferred, - ], - ), - lines_between: [], - body_pattern: @11-15 Identifier { - ident: "#!0_stmt", - }, - body_expr: @11-14 Var { - module_name: "", - ident: "foo", - }, - }, - ], - }, - @11-14 Var { - module_name: "", - ident: "#!0_stmt", - }, - ), - @11-14 Closure( - [ - @11-15 Underscore( - "#!stmt", - ), - ], - @21-26 Apply( - @21-23 Var { - module_name: "", - ident: "ok", - }, - [ - @24-26 Record( - [], - ), - ], - Space, - ), - ), - ], - BangSuffix, - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__body_parens_apply.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__body_parens_apply.snap deleted file mode 100644 index bbe698549dd..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__body_parens_apply.snap +++ /dev/null @@ -1,95 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-42, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @16-35 Apply( - @16-35 Var { - module_name: "Task", - ident: "await", - }, - [ - Var { - module_name: "", - ident: "sayMultiple", - }, - @16-35 Closure( - [ - Identifier { - ident: "#!0_arg", - }, - ], - @16-35 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @16-35, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @11-13 Identifier { - ident: "do", - }, - @16-35 Apply( - @17-29 ParensAround( - Var { - module_name: "", - ident: "#!0_arg", - }, - ), - [ - @31-35 Str( - PlainLine( - "hi", - ), - ), - ], - Space, - ), - ), - ], - }, - @40-42 Var { - module_name: "", - ident: "do", - }, - ), - ), - ], - BangSuffix, - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap deleted file mode 100644 index 9e57e9e0bf9..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap +++ /dev/null @@ -1,163 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-69, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @11-69 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @15-57, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @11-12 Identifier { - ident: "x", - }, - @15-57 Closure( - [ - @16-19 Identifier { - ident: "msg", - }, - ], - @31-43 Apply( - @31-43 Var { - module_name: "Task", - ident: "await", - }, - [ - @31-43 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @31-43, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @31-43 Identifier { - ident: "#!0_stmt", - }, - ann_type: @31-43 Apply( - "", - "Task", - [ - @31-43 Record { - fields: [], - ext: None, - }, - @31-43 Inferred, - ], - ), - lines_between: [], - body_pattern: @31-43 Identifier { - ident: "#!0_stmt", - }, - body_expr: @31-43 Apply( - @31-43 Var { - module_name: "", - ident: "line", - }, - [ - @31-34 Var { - module_name: "", - ident: "msg", - }, - ], - BinOp( - Pizza, - ), - ), - }, - ], - }, - @31-43 Var { - module_name: "", - ident: "#!0_stmt", - }, - ), - @31-43 Closure( - [ - @31-43 Underscore( - "#!stmt", - ), - ], - @52-57 Apply( - @52-54 Var { - module_name: "", - ident: "ok", - }, - [ - @55-57 Record( - [], - ), - ], - Space, - ), - ), - ], - BangSuffix, - ), - ), - ), - ], - }, - @63-69 Apply( - @63-64 Var { - module_name: "", - ident: "x", - }, - [ - @65-69 Str( - PlainLine( - "hi", - ), - ), - ], - Space, - ), - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap deleted file mode 100644 index 283fc71c0d4..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap +++ /dev/null @@ -1,164 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-114, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @11-114 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @39-101, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @11-12 Identifier { - ident: "x", - }, - ann_type: @15-30 Function( - [ - @15-18 Apply( - "", - "Str", - [], - ), - ], - Pure, - @22-30 Apply( - "", - "Task", - [ - @27-28 Inferred, - @29-30 Inferred, - ], - ), - ), - lines_between: [ - Newline, - ], - body_pattern: @35-36 Identifier { - ident: "x", - }, - body_expr: @39-101 Closure( - [ - @40-43 Identifier { - ident: "msg", - }, - ], - @56-91 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @82-91, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @56-57 Identifier { - ident: "#!0_expr", - }, - ann_type: @60-69 Apply( - "", - "Task", - [ - @60-69 Apply( - "", - "Task", - [ - @65-67 Record { - fields: [], - ext: None, - }, - @68-69 Inferred, - ], - ), - @60-69 Inferred, - ], - ), - lines_between: [], - body_pattern: @78-79 Identifier { - ident: "#!0_expr", - }, - body_expr: @82-91 Apply( - @82-91 Var { - module_name: "", - ident: "line", - }, - [ - @88-91 Var { - module_name: "", - ident: "msg", - }, - ], - Space, - ), - }, - ], - }, - @82-91 Var { - module_name: "", - ident: "#!0_expr", - }, - ), - ), - }, - ], - }, - @107-114 Apply( - @107-108 Var { - module_name: "", - ident: "x", - }, - [ - @109-114 Str( - PlainLine( - "foo", - ), - ), - ], - Space, - ), - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap deleted file mode 100644 index 7269bfe039e..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap +++ /dev/null @@ -1,288 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-143, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @12-143 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @56-119, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @12-15 Identifier { - ident: "foo", - }, - ann_type: @18-45 Function( - [ - @18-21 Apply( - "", - "Str", - [], - ), - @23-25 Record { - fields: [], - ext: None, - }, - @27-30 Apply( - "", - "Str", - [], - ), - ], - Pure, - @34-45 Apply( - "", - "Task", - [ - @39-41 Record { - fields: [], - ext: None, - }, - @42-45 Apply( - "", - "I32", - [], - ), - ], - ), - ), - lines_between: [ - Newline, - ], - body_pattern: @50-53 Identifier { - ident: "foo", - }, - body_expr: @56-119 Closure( - [ - @57-58 Identifier { - ident: "a", - }, - @60-61 Underscore( - "", - ), - @63-64 Identifier { - ident: "b", - }, - ], - @76-83 Apply( - @76-83 Var { - module_name: "Task", - ident: "await", - }, - [ - @76-83 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @76-83, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @76-83 Identifier { - ident: "#!1_stmt", - }, - ann_type: @76-83 Apply( - "", - "Task", - [ - @76-83 Record { - fields: [], - ext: None, - }, - @76-83 Inferred, - ], - ), - lines_between: [], - body_pattern: @76-83 Identifier { - ident: "#!1_stmt", - }, - body_expr: @76-83 Apply( - @76-83 Var { - module_name: "", - ident: "line", - }, - [ - @82-83 Var { - module_name: "", - ident: "a", - }, - ], - Space, - ), - }, - ], - }, - @76-83 Var { - module_name: "", - ident: "#!1_stmt", - }, - ), - @76-83 Closure( - [ - @76-83 Underscore( - "#!stmt", - ), - ], - @92-99 Apply( - @92-99 Var { - module_name: "Task", - ident: "await", - }, - [ - @92-99 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @92-99, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @92-99 Identifier { - ident: "#!0_stmt", - }, - ann_type: @92-99 Apply( - "", - "Task", - [ - @92-99 Record { - fields: [], - ext: None, - }, - @92-99 Inferred, - ], - ), - lines_between: [], - body_pattern: @92-99 Identifier { - ident: "#!0_stmt", - }, - body_expr: @92-99 Apply( - @92-99 Var { - module_name: "", - ident: "line", - }, - [ - @98-99 Var { - module_name: "", - ident: "b", - }, - ], - Space, - ), - }, - ], - }, - @92-99 Var { - module_name: "", - ident: "#!0_stmt", - }, - ), - @92-99 Closure( - [ - @92-99 Underscore( - "#!stmt", - ), - ], - @109-119 Apply( - @109-116 Var { - module_name: "Task", - ident: "ok", - }, - [ - @117-119 Record( - [], - ), - ], - Space, - ), - ), - ], - BangSuffix, - ), - ), - ], - BangSuffix, - ), - ), - }, - ], - }, - @125-143 Apply( - @125-128 Var { - module_name: "", - ident: "foo", - }, - [ - @129-134 Str( - PlainLine( - "bar", - ), - ), - @135-137 Record( - [], - ), - @138-143 Str( - PlainLine( - "baz", - ), - ), - ], - Space, - ), - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_expr.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_expr.snap deleted file mode 100644 index b09bbd401a2..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_expr.snap +++ /dev/null @@ -1,147 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-28, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @11-28 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @16-27, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @16-27 Identifier { - ident: "1", - }, - @16-27 ParensAround( - Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @21-26, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @21-26 Identifier { - ident: "0", - }, - @21-26 ParensAround( - Apply( - @23-24 Var { - module_name: "Num", - ident: "add", - }, - [ - @21-22 Num( - "1", - ), - @25-26 Num( - "1", - ), - ], - BinOp( - Plus, - ), - ), - ), - ), - ], - }, - @16-27 LowLevelDbg( - ( - "test.roc:3", - " ", - ), - @21-26 Apply( - @21-26 Var { - module_name: "Inspect", - ident: "to_str", - }, - [ - @21-26 Var { - module_name: "", - ident: "0", - }, - ], - Space, - ), - @21-26 Var { - module_name: "", - ident: "0", - }, - ), - ), - ), - ), - ], - }, - @11-28 LowLevelDbg( - ( - "test.roc:2", - "n =\n ", - ), - @16-27 Apply( - @16-27 Var { - module_name: "Inspect", - ident: "to_str", - }, - [ - @16-27 Var { - module_name: "", - ident: "1", - }, - ], - Space, - ), - @16-27 Var { - module_name: "", - ident: "1", - }, - ), - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_simple.snap deleted file mode 100644 index 706625b7ab0..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_simple.snap +++ /dev/null @@ -1,82 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-49, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @17-23 Apply( - @17-23 Var { - module_name: "Task", - ident: "await", - }, - [ - @17-23 Var { - module_name: "", - ident: "getFoo", - }, - @17-23 Closure( - [ - @11-14 Identifier { - ident: "foo", - }, - ], - @29-36 LowLevelDbg( - ( - "test.roc:3", - " ", - ), - @33-36 Apply( - @33-36 Var { - module_name: "Inspect", - ident: "to_str", - }, - [ - @33-36 Var { - module_name: "", - ident: "foo", - }, - ], - Space, - ), - @41-49 Apply( - @41-49 Var { - module_name: "", - ident: "bar", - }, - [ - @46-49 Var { - module_name: "", - ident: "foo", - }, - ], - Space, - ), - ), - ), - ], - BangSuffix, - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_stmt_arg.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_stmt_arg.snap deleted file mode 100644 index f7cc5f82347..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_stmt_arg.snap +++ /dev/null @@ -1,73 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-24, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @11-17 Apply( - @11-17 Var { - module_name: "Task", - ident: "await", - }, - [ - @15-16 Var { - module_name: "", - ident: "a", - }, - @11-17 Closure( - [ - @15-16 Identifier { - ident: "#!0_arg", - }, - ], - @11-17 LowLevelDbg( - ( - "test.roc:2", - "i", - ), - @15-16 Apply( - @15-16 Var { - module_name: "Inspect", - ident: "to_str", - }, - [ - @15-16 Var { - module_name: "", - ident: "#!0_arg", - }, - ], - Space, - ), - @23-24 Var { - module_name: "", - ident: "b", - }, - ), - ), - ], - BangSuffix, - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deep_when.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deep_when.snap deleted file mode 100644 index 9398768a220..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deep_when.snap +++ /dev/null @@ -1,66 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-99, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @11-99 When( - @16-17 Var { - module_name: "", - ident: "a", - }, - [ - WhenBranch { - patterns: [ - @29-30 NumLiteral( - "0", - ), - ], - value: @46-99 When( - @51-52 Var { - module_name: "", - ident: "b", - }, - [ - WhenBranch { - patterns: [ - @72-73 NumLiteral( - "1", - ), - ], - value: @97-98 Var { - module_name: "", - ident: "c", - }, - guard: None, - }, - ], - ), - guard: None, - }, - ], - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap deleted file mode 100644 index 2c8f41d387c..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap +++ /dev/null @@ -1,191 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - EitherIndex(2147483649), - ], - regions: [ - @0-54, - @56-98, - ], - space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 2 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 2, length: 1 }, - ], - spaces: [ - Newline, - Newline, - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @11-54 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @15-20, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @11-12 Identifier { - ident: "a", - }, - @15-20 Str( - PlainLine( - "Foo", - ), - ), - ), - ], - }, - @11-54 Apply( - @11-54 Var { - module_name: "Task", - ident: "await", - }, - [ - @25-39 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @25-39, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @25-39 Identifier { - ident: "#!1_stmt", - }, - ann_type: @25-39 Apply( - "", - "Task", - [ - @25-39 Record { - fields: [], - ext: None, - }, - @25-39 Inferred, - ], - ), - lines_between: [], - body_pattern: @25-39 Identifier { - ident: "#!1_stmt", - }, - body_expr: @25-39 Apply( - @25-39 Var { - module_name: "Stdout", - ident: "line", - }, - [ - @38-39 Var { - module_name: "", - ident: "a", - }, - ], - Space, - ), - }, - ], - }, - @11-54 Var { - module_name: "", - ident: "#!1_stmt", - }, - ), - @11-54 Closure( - [ - @25-39 Underscore( - "#!stmt", - ), - ], - @45-53 Var { - module_name: "", - ident: "printBar", - }, - ), - ], - BangSuffix, - ), - ), - ), - Body( - @56-64 Identifier { - ident: "printBar", - }, - @71-98 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @75-80, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @71-72 Identifier { - ident: "b", - }, - @75-80 Str( - PlainLine( - "Bar", - ), - ), - ), - ], - }, - @85-98 Apply( - @85-96 Var { - module_name: "Stdout", - ident: "line", - }, - [ - @97-98 Var { - module_name: "", - ident: "b", - }, - ], - Space, - ), - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deps_final_expr.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deps_final_expr.snap deleted file mode 100644 index 972c2983881..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deps_final_expr.snap +++ /dev/null @@ -1,105 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-158, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @11-158 When( - @16-17 Var { - module_name: "", - ident: "x", - }, - [ - WhenBranch { - patterns: [ - @29-30 Tag( - "A", - ), - ], - value: @46-130 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @50-52, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @46-47 Identifier { - ident: "y", - }, - @50-52 Num( - "42", - ), - ), - ], - }, - @66-130 If { - if_thens: [ - ( - @69-70 Var { - module_name: "", - ident: "a", - }, - @92-93 Var { - module_name: "", - ident: "b", - }, - ), - ], - final_else: @128-129 Var { - module_name: "", - ident: "c", - }, - indented_else: false, - }, - ), - guard: None, - }, - WhenBranch { - patterns: [ - @139-140 Tag( - "B", - ), - ], - value: @156-157 Var { - module_name: "", - ident: "d", - }, - guard: None, - }, - ], - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__expect_then_bang.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__expect_then_bang.snap deleted file mode 100644 index c5d6bfe6d9f..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__expect_then_bang.snap +++ /dev/null @@ -1,74 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-31, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @11-31 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @11-24, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - Expect { - condition: @18-24 Apply( - @20-22 Var { - module_name: "Bool", - ident: "is_eq", - }, - [ - @18-19 Num( - "1", - ), - @23-24 Num( - "2", - ), - ], - BinOp( - Equals, - ), - ), - preceding_comment: @11-11, - }, - ], - }, - @29-30 Var { - module_name: "", - ident: "x", - }, - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap deleted file mode 100644 index 22b4f957cb6..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap +++ /dev/null @@ -1,406 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-307, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @11-307 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - EitherIndex(2147483649), - EitherIndex(2147483650), - ], - regions: [ - @20-37, - @53-68, - @109-298, - ], - space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 1 }, - Slice { start: 1, length: 1 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 1, length: 0 }, - Slice { start: 2, length: 0 }, - ], - spaces: [ - Newline, - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @11-17 Identifier { - ident: "isTrue", - }, - @20-37 Apply( - @20-27 Var { - module_name: "Task", - ident: "ok", - }, - [ - @28-37 Var { - module_name: "Bool", - ident: "true", - }, - ], - Space, - ), - ), - Body( - @42-50 Identifier { - ident: "isFalsey", - }, - @53-68 Closure( - [ - @54-55 Identifier { - ident: "x", - }, - ], - @59-68 Apply( - @59-66 Var { - module_name: "Task", - ident: "ok", - }, - [ - @67-68 Var { - module_name: "", - ident: "x", - }, - ], - Space, - ), - ), - ), - AnnotatedBody { - ann_pattern: @73-76 Identifier { - ident: "msg", - }, - ann_type: @79-90 Apply( - "", - "Task", - [ - @84-86 Record { - fields: [], - ext: None, - }, - @87-90 Apply( - "", - "I32", - [], - ), - ], - ), - lines_between: [ - Newline, - ], - body_pattern: @95-98 Identifier { - ident: "msg", - }, - body_expr: Apply( - Var { - module_name: "Task", - ident: "await", - }, - [ - Var { - module_name: "", - ident: "isTrue", - }, - Closure( - [ - Identifier { - ident: "#!1_arg", - }, - ], - @109-298 If { - if_thens: [ - ( - @112-122 Apply( - @112-113 Var { - module_name: "Bool", - ident: "not", - }, - [ - @114-121 ParensAround( - Var { - module_name: "", - ident: "#!1_arg", - }, - ), - ], - UnaryOp( - Not, - ), - ), - @140-152 Apply( - @140-152 Var { - module_name: "Task", - ident: "await", - }, - [ - @140-152 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @140-152, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @140-152 Identifier { - ident: "#!0_stmt", - }, - ann_type: @140-152 Apply( - "", - "Task", - [ - @140-152 Record { - fields: [], - ext: None, - }, - @140-152 Inferred, - ], - ), - lines_between: [], - body_pattern: @140-152 Identifier { - ident: "#!0_stmt", - }, - body_expr: @140-152 Apply( - @140-152 Var { - module_name: "", - ident: "line", - }, - [ - @146-152 Str( - PlainLine( - "fail", - ), - ), - ], - Space, - ), - }, - ], - }, - @140-152 Var { - module_name: "", - ident: "#!0_stmt", - }, - ), - @140-152 Closure( - [ - @140-152 Underscore( - "#!stmt", - ), - ], - @165-170 Apply( - @165-168 Var { - module_name: "", - ident: "err", - }, - [ - @169-170 Num( - "1", - ), - ], - Space, - ), - ), - ], - BangSuffix, - ), - ), - ], - final_else: Apply( - Var { - module_name: "Task", - ident: "await", - }, - [ - Apply( - Var { - module_name: "", - ident: "isFalsey", - }, - [ - @198-208 Var { - module_name: "Bool", - ident: "false", - }, - ], - Space, - ), - Closure( - [ - Identifier { - ident: "#!3_arg", - }, - ], - @109-298 If { - if_thens: [ - ( - @187-209 ParensAround( - Var { - module_name: "", - ident: "#!3_arg", - }, - ), - @227-239 Apply( - @227-239 Var { - module_name: "Task", - ident: "await", - }, - [ - @227-239 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @227-239, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @227-239 Identifier { - ident: "#!2_stmt", - }, - ann_type: @227-239 Apply( - "", - "Task", - [ - @227-239 Record { - fields: [], - ext: None, - }, - @227-239 Inferred, - ], - ), - lines_between: [], - body_pattern: @227-239 Identifier { - ident: "#!2_stmt", - }, - body_expr: @227-239 Apply( - @227-239 Var { - module_name: "", - ident: "line", - }, - [ - @233-239 Str( - PlainLine( - "nope", - ), - ), - ], - Space, - ), - }, - ], - }, - @227-239 Var { - module_name: "", - ident: "#!2_stmt", - }, - ), - @227-239 Closure( - [ - @227-239 Underscore( - "#!stmt", - ), - ], - @252-257 Apply( - @252-254 Var { - module_name: "", - ident: "ok", - }, - [ - @255-257 Record( - [], - ), - ], - Space, - ), - ), - ], - BangSuffix, - ), - ), - ], - final_else: @283-298 Apply( - @283-298 Var { - module_name: "", - ident: "line", - }, - [ - @289-298 Str( - PlainLine( - "success", - ), - ), - ], - Space, - ), - indented_else: false, - }, - ), - ], - BangSuffix, - ), - indented_else: false, - }, - ), - ], - BangSuffix, - ), - }, - ], - }, - @304-307 Var { - module_name: "", - ident: "msg", - }, - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_simple.snap deleted file mode 100644 index 8c0bd2adf9e..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_simple.snap +++ /dev/null @@ -1,196 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-189, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @11-189 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - EitherIndex(2147483649), - ], - regions: [ - @20-37, - @52-70, - ], - space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 1 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 1, length: 0 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @11-17 Identifier { - ident: "isTrue", - }, - @20-37 Apply( - @20-27 Var { - module_name: "Task", - ident: "ok", - }, - [ - @28-37 Var { - module_name: "Bool", - ident: "true", - }, - ], - Space, - ), - ), - Body( - @42-49 Identifier { - ident: "isFalse", - }, - @52-70 Apply( - @52-59 Var { - module_name: "Task", - ident: "ok", - }, - [ - @60-70 Var { - module_name: "Bool", - ident: "false", - }, - ], - Space, - ), - ), - ], - }, - @79-86 Apply( - @79-86 Var { - module_name: "Task", - ident: "await", - }, - [ - @79-86 Var { - module_name: "", - ident: "isFalse", - }, - @79-86 Closure( - [ - @79-86 Identifier { - ident: "#!0_arg", - }, - ], - @76-189 If { - if_thens: [ - ( - @79-86 Var { - module_name: "", - ident: "#!0_arg", - }, - @101-112 Apply( - @101-105 Var { - module_name: "", - ident: "line", - }, - [ - @106-112 Str( - PlainLine( - "fail", - ), - ), - ], - Space, - ), - ), - ], - final_else: @125-131 Apply( - @125-131 Var { - module_name: "Task", - ident: "await", - }, - [ - @125-131 Var { - module_name: "", - ident: "isTrue", - }, - @125-131 Closure( - [ - @125-131 Identifier { - ident: "#!1_arg", - }, - ], - @76-189 If { - if_thens: [ - ( - @125-131 Var { - module_name: "", - ident: "#!1_arg", - }, - @146-160 Apply( - @146-150 Var { - module_name: "", - ident: "line", - }, - [ - @151-160 Str( - PlainLine( - "success", - ), - ), - ], - Space, - ), - ), - ], - final_else: @178-189 Apply( - @178-182 Var { - module_name: "", - ident: "line", - }, - [ - @183-189 Str( - PlainLine( - "fail", - ), - ), - ], - Space, - ), - indented_else: false, - }, - ), - ], - BangSuffix, - ), - indented_else: false, - }, - ), - ], - BangSuffix, - ), - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__issue_7081.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__issue_7081.snap deleted file mode 100644 index 9396e9fac35..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__issue_7081.snap +++ /dev/null @@ -1,281 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - EitherIndex(2147483649), - EitherIndex(2147483650), - ], - regions: [ - @0-80, - @82-227, - @229-266, - ], - space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 2 }, - Slice { start: 2, length: 2 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 2, length: 0 }, - Slice { start: 4, length: 1 }, - ], - spaces: [ - Newline, - Newline, - Newline, - Newline, - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-3 Identifier { - ident: "inc", - }, - @6-80 Closure( - [ - @7-8 Identifier { - ident: "i", - }, - ], - @16-80 If { - if_thens: [ - ( - @19-24 Apply( - @21-22 Var { - module_name: "Num", - ident: "is_gt", - }, - [ - @19-20 Var { - module_name: "", - ident: "i", - }, - @23-24 Num( - "2", - ), - ], - BinOp( - GreaterThan, - ), - ), - @38-52 Apply( - @38-41 Tag( - "Err", - ), - [ - @42-52 Tag( - "MaxReached", - ), - ], - Space, - ), - ), - ], - final_else: @70-80 Apply( - @70-72 Tag( - "Ok", - ), - [ - @74-79 ParensAround( - Apply( - @76-77 Var { - module_name: "Num", - ident: "add", - }, - [ - @74-75 Var { - module_name: "", - ident: "i", - }, - @78-79 Num( - "1", - ), - ], - BinOp( - Plus, - ), - ), - ), - ], - Space, - ), - indented_else: false, - }, - ), - ), - Expect { - condition: @93-227 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - EitherIndex(2147483649), - ], - regions: [ - @99-189, - @203-208, - ], - space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 1 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 1, length: 0 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @93-96 Identifier { - ident: "run", - }, - @99-189 Closure( - [ - @100-101 Identifier { - ident: "i", - }, - ], - @113-189 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @132-172, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @113-117 Identifier { - ident: "newi", - }, - @132-172 LowLevelTry( - @132-172 Apply( - @169-172 Var { - module_name: "", - ident: "inc", - }, - [ - @132-152 LowLevelTry( - @132-152 Apply( - @149-152 Var { - module_name: "", - ident: "inc", - }, - [ - @132-133 Var { - module_name: "", - ident: "i", - }, - ], - Try, - ), - OperatorSuffix, - ), - ], - Try, - ), - OperatorSuffix, - ), - ), - ], - }, - @182-189 Apply( - @182-184 Tag( - "Ok", - ), - [ - @185-189 Var { - module_name: "", - ident: "newi", - }, - ], - Space, - ), - ), - ), - ), - Body( - @194-200 Identifier { - ident: "result", - }, - @203-208 Apply( - @203-206 Var { - module_name: "", - ident: "run", - }, - [ - @207-208 Num( - "0", - ), - ], - Space, - ), - ), - ], - }, - @213-227 Apply( - @220-222 Var { - module_name: "Bool", - ident: "is_eq", - }, - [ - @213-219 Var { - module_name: "", - ident: "result", - }, - @223-227 Apply( - @223-225 Tag( - "Ok", - ), - [ - @226-227 Num( - "2", - ), - ], - Space, - ), - ], - BinOp( - Equals, - ), - ), - ), - preceding_comment: @82-82, - }, - Body( - @229-233 Identifier { - ident: "main", - }, - @240-266 Apply( - @240-266 Var { - module_name: "Stdout", - ident: "line", - }, - [ - @253-266 Str( - PlainLine( - "Hello world", - ), - ), - ], - Space, - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__issue_7103.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__issue_7103.snap deleted file mode 100644 index 21efe374ca4..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__issue_7103.snap +++ /dev/null @@ -1,76 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - EitherIndex(2147483649), - ], - regions: [ - @0-33, - @35-45, - ], - space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 2 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 2, length: 1 }, - ], - spaces: [ - Newline, - Newline, - Newline, - ], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @0-3 Identifier { - ident: "run", - }, - ann_type: @6-15 Apply( - "", - "Task", - [ - @11-13 Record { - fields: [], - ext: None, - }, - @14-15 Inferred, - ], - ), - lines_between: [ - Newline, - ], - body_pattern: @16-19 Identifier { - ident: "run", - }, - body_expr: @22-33 Apply( - @22-33 Var { - module_name: "", - ident: "line", - }, - [ - @28-33 Str( - PlainLine( - "foo", - ), - ), - ], - Space, - ), - }, - Body( - @35-39 Identifier { - ident: "main", - }, - @42-45 Var { - module_name: "", - ident: "run", - }, - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_stmt_not_top_level_suffixed.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_stmt_not_top_level_suffixed.snap deleted file mode 100644 index 6de13d2d050..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_stmt_not_top_level_suffixed.snap +++ /dev/null @@ -1,91 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-26, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @11-26 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @15-17, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @11-12 Identifier { - ident: "x", - }, - @15-17 Num( - "42", - ), - ), - ], - }, - @11-26 Apply( - @11-26 Var { - module_name: "Task", - ident: "await", - }, - [ - @24-25 Var { - module_name: "", - ident: "b", - }, - @11-26 Closure( - [ - @24-25 Identifier { - ident: "#!0_arg", - }, - ], - @22-26 Apply( - @22-23 Var { - module_name: "", - ident: "a", - }, - [ - @24-25 Var { - module_name: "", - ident: "#!0_arg", - }, - ], - Space, - ), - ), - ], - BangSuffix, - ), - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap deleted file mode 100644 index c40fb213b13..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap +++ /dev/null @@ -1,162 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-33, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @11-14 Apply( - @11-14 Var { - module_name: "Task", - ident: "await", - }, - [ - @11-14 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @11-14, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @11-15 Identifier { - ident: "#!2_stmt", - }, - ann_type: @11-15 Apply( - "", - "Task", - [ - @11-15 Record { - fields: [], - ext: None, - }, - @11-15 Inferred, - ], - ), - lines_between: [], - body_pattern: @11-15 Identifier { - ident: "#!2_stmt", - }, - body_expr: @11-14 Var { - module_name: "", - ident: "foo", - }, - }, - ], - }, - @11-14 Var { - module_name: "", - ident: "#!2_stmt", - }, - ), - @11-14 Closure( - [ - @11-15 Underscore( - "#!stmt", - ), - ], - @20-23 Apply( - @20-23 Var { - module_name: "Task", - ident: "await", - }, - [ - @20-23 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @20-23, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @20-24 Identifier { - ident: "#!1_stmt", - }, - ann_type: @20-24 Apply( - "", - "Task", - [ - @20-24 Record { - fields: [], - ext: None, - }, - @20-24 Inferred, - ], - ), - lines_between: [], - body_pattern: @20-24 Identifier { - ident: "#!1_stmt", - }, - body_expr: @20-23 Var { - module_name: "", - ident: "bar", - }, - }, - ], - }, - @20-23 Var { - module_name: "", - ident: "#!1_stmt", - }, - ), - @20-23 Closure( - [ - @20-24 Underscore( - "#!stmt", - ), - ], - @29-32 Var { - module_name: "", - ident: "baz", - }, - ), - ], - BangSuffix, - ), - ), - ], - BangSuffix, - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_single.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_single.snap deleted file mode 100644 index f493cbcb22b..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_single.snap +++ /dev/null @@ -1,52 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-26, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @7-26 Apply( - @7-26 Var { - module_name: "", - ident: "foo", - }, - [ - @12-17 Str( - PlainLine( - "bar", - ), - ), - @18-20 Record( - [], - ), - @21-26 Str( - PlainLine( - "baz", - ), - ), - ], - Space, - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap deleted file mode 100644 index 6c1b1b859a5..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap +++ /dev/null @@ -1,148 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-72, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @11-23 Apply( - @11-23 Var { - module_name: "Task", - ident: "await", - }, - [ - @11-23 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @11-23, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @11-23 Identifier { - ident: "#!0_stmt", - }, - ann_type: @11-23 Apply( - "", - "Task", - [ - @11-23 Record { - fields: [], - ext: None, - }, - @11-23 Inferred, - ], - ), - lines_between: [], - body_pattern: @11-23 Identifier { - ident: "#!0_stmt", - }, - body_expr: @11-23 Apply( - @11-23 Var { - module_name: "", - ident: "line", - }, - [ - @17-23 Str( - PlainLine( - "Ahoy", - ), - ), - ], - Space, - ), - }, - ], - }, - @11-23 Var { - module_name: "", - ident: "#!0_stmt", - }, - ), - @11-23 Closure( - [ - @11-23 Underscore( - "#!stmt", - ), - ], - @33-56 Apply( - @33-56 Var { - module_name: "Task", - ident: "await", - }, - [ - @33-56 Apply( - @33-56 Var { - module_name: "Stdout", - ident: "line", - }, - [ - @33-40 Str( - PlainLine( - "There", - ), - ), - ], - BinOp( - Pizza, - ), - ), - @33-56 Closure( - [ - @28-30 RecordDestructure( - [], - ), - ], - @62-72 Apply( - @62-69 Var { - module_name: "Task", - ident: "ok", - }, - [ - @70-72 Record( - [], - ), - ], - Space, - ), - ), - ], - BangSuffix, - ), - ), - ], - BangSuffix, - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_def_first_suffixed.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_def_first_suffixed.snap deleted file mode 100644 index 745fabf31a3..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_def_first_suffixed.snap +++ /dev/null @@ -1,102 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-51, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @11-51 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @17-24, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @11-14 Identifier { - ident: "msg", - }, - @17-24 Str( - PlainLine( - "hello", - ), - ), - ), - ], - }, - @11-51 Apply( - @11-51 Var { - module_name: "Task", - ident: "await", - }, - [ - @33-41 Apply( - @33-41 Var { - module_name: "", - ident: "foo", - }, - [ - @38-41 Var { - module_name: "", - ident: "msg", - }, - ], - Space, - ), - @11-51 Closure( - [ - @29-30 Identifier { - ident: "x", - }, - ], - @46-51 Apply( - @46-49 Var { - module_name: "", - ident: "bar", - }, - [ - @50-51 Var { - module_name: "", - ident: "x", - }, - ], - Space, - ), - ), - ], - BangSuffix, - ), - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap deleted file mode 100644 index 6f2633394c4..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap +++ /dev/null @@ -1,105 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-24, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @11-16 Apply( - @11-16 Var { - module_name: "Task", - ident: "await", - }, - [ - @11-16 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @11-16, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @11-16 Identifier { - ident: "#!1_stmt", - }, - ann_type: @11-16 Apply( - "", - "Task", - [ - @11-16 Apply( - "", - "Task", - [ - @11-16 Record { - fields: [], - ext: None, - }, - @11-16 Inferred, - ], - ), - @11-16 Inferred, - ], - ), - lines_between: [], - body_pattern: @11-16 Identifier { - ident: "#!1_stmt", - }, - body_expr: @11-16 Var { - module_name: "", - ident: "foo", - }, - }, - ], - }, - @11-16 Var { - module_name: "", - ident: "#!1_stmt", - }, - ), - @11-16 Closure( - [ - @11-16 Underscore( - "#!stmt", - ), - ], - @21-24 Var { - module_name: "", - ident: "bar", - }, - ), - ], - BangSuffix, - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_complex.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_complex.snap deleted file mode 100644 index d0fff1ca95b..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_complex.snap +++ /dev/null @@ -1,118 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-61, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @15-43 Apply( - @15-43 Var { - module_name: "Task", - ident: "await", - }, - [ - Apply( - Var { - module_name: "", - ident: "bar", - }, - [ - @26-29 Var { - module_name: "", - ident: "baz", - }, - ], - Space, - ), - @15-43 Closure( - [ - Identifier { - ident: "#!0_arg", - }, - ], - @15-43 Apply( - @15-43 Var { - module_name: "Task", - ident: "await", - }, - [ - @15-43 Apply( - @15-43 Var { - module_name: "", - ident: "foo", - }, - [ - @21-29 ParensAround( - Var { - module_name: "", - ident: "#!0_arg", - }, - ), - @32-42 ParensAround( - Apply( - @32-36 Var { - module_name: "", - ident: "blah", - }, - [ - @37-42 Var { - module_name: "", - ident: "stuff", - }, - ], - Space, - ), - ), - ], - Space, - ), - @15-43 Closure( - [ - @11-12 Identifier { - ident: "z", - }, - ], - @48-61 Apply( - @48-59 Var { - module_name: "", - ident: "doSomething", - }, - [ - @60-61 Var { - module_name: "", - ident: "z", - }, - ], - Space, - ), - ), - ], - BangSuffix, - ), - ), - ], - BangSuffix, - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_defs.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_defs.snap deleted file mode 100644 index a78050a3149..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_defs.snap +++ /dev/null @@ -1,92 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-49, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @11-49 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @23-42, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @11-12 Identifier { - ident: "x", - }, - @27-28 Apply( - @27-28 Var { - module_name: "Task", - ident: "await", - }, - [ - @27-28 Var { - module_name: "", - ident: "b", - }, - @27-28 Closure( - [ - @23-24 Identifier { - ident: "a", - }, - ], - @38-42 Apply( - @38-42 Var { - module_name: "", - ident: "c", - }, - [ - @41-42 Var { - module_name: "", - ident: "a", - }, - ], - Space, - ), - ), - ], - BangSuffix, - ), - ), - ], - }, - @48-49 Var { - module_name: "", - ident: "x", - }, - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_simple.snap deleted file mode 100644 index d9cfc3dbc01..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_simple.snap +++ /dev/null @@ -1,65 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-22, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-3 Identifier { - ident: "run", - }, - @6-22 Apply( - @6-22 Var { - module_name: "Task", - ident: "await", - }, - [ - Var { - module_name: "", - ident: "nextMsg", - }, - @6-22 Closure( - [ - Identifier { - ident: "#!0_arg", - }, - ], - @6-22 Apply( - @6-22 Var { - module_name: "", - ident: "line", - }, - [ - @13-21 ParensAround( - Var { - module_name: "", - ident: "#!0_arg", - }, - ), - ], - Space, - ), - ), - ], - BangSuffix, - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__pizza_dbg.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__pizza_dbg.snap deleted file mode 100644 index d064a11fd38..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__pizza_dbg.snap +++ /dev/null @@ -1,143 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-51, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @11-51 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @11-40, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @11-40 Identifier { - ident: "1", - }, - @11-40 Apply( - @31-38 Var { - module_name: "Num", - ident: "add", - }, - [ - @11-23 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @11-12, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @11-12 Identifier { - ident: "0", - }, - @11-12 Num( - "1", - ), - ), - ], - }, - @11-23 LowLevelDbg( - ( - "test.roc:2", - " ", - ), - @11-12 Apply( - @11-12 Var { - module_name: "Inspect", - ident: "to_str", - }, - [ - @11-12 Var { - module_name: "", - ident: "0", - }, - ], - Space, - ), - @11-12 Var { - module_name: "", - ident: "0", - }, - ), - ), - @39-40 Num( - "2", - ), - ], - BinOp( - Pizza, - ), - ), - ), - ], - }, - @11-51 LowLevelDbg( - ( - "test.roc:2", - " main =\n 1\n ", - ), - @11-40 Apply( - @11-40 Var { - module_name: "Inspect", - ident: "to_str", - }, - [ - @11-40 Var { - module_name: "", - ident: "1", - }, - ], - Space, - ), - @11-40 Var { - module_name: "", - ident: "1", - }, - ), - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_double_question.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_double_question.snap deleted file mode 100644 index f150064f351..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_double_question.snap +++ /dev/null @@ -1,181 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-86, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @11-70 Apply( - @11-70 Var { - module_name: "Task", - ident: "await", - }, - [ - @11-70 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @11-70, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @11-70 Identifier { - ident: "#!0_stmt", - }, - ann_type: @11-70 Apply( - "", - "Task", - [ - @11-70 Record { - fields: [], - ext: None, - }, - @11-70 Inferred, - ], - ), - lines_between: [], - body_pattern: @11-70 Identifier { - ident: "#!0_stmt", - }, - body_expr: @11-70 Apply( - @11-70 Var { - module_name: "", - ident: "line", - }, - [ - @11-57 Apply( - @47-57 Var { - module_name: "Num", - ident: "to_str", - }, - [ - @11-39 Apply( - @24-39 When( - @24-32 Var { - module_name: "Str", - ident: "toU8", - }, - [ - WhenBranch { - patterns: [ - @24-32 PncApply( - @24-32 Tag( - "Ok", - ), - [ - @24-32 Identifier { - ident: "success_BRANCH1_24_32", - }, - ], - ), - ], - value: @24-32 Var { - module_name: "", - ident: "success_BRANCH1_24_32", - }, - guard: None, - }, - WhenBranch { - patterns: [ - @36-39 PncApply( - @24-32 Tag( - "Err", - ), - [ - @36-39 Underscore( - "", - ), - ], - ), - ], - value: @36-39 Num( - "255", - ), - guard: None, - }, - ], - ), - [ - @11-16 Str( - PlainLine( - "123", - ), - ), - ], - BinOp( - Pizza, - ), - ), - ], - BinOp( - Pizza, - ), - ), - ], - BinOp( - Pizza, - ), - ), - }, - ], - }, - @11-70 Var { - module_name: "", - ident: "#!0_stmt", - }, - ), - @11-70 Closure( - [ - @11-70 Underscore( - "#!stmt", - ), - ], - @76-86 Apply( - @76-83 Var { - module_name: "Task", - ident: "ok", - }, - [ - @84-86 Record( - [], - ), - ], - Space, - ), - ), - ], - BangSuffix, - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap deleted file mode 100644 index 5b304a726c9..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap +++ /dev/null @@ -1,134 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-73, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @11-57 Apply( - @11-57 Var { - module_name: "Task", - ident: "await", - }, - [ - @11-57 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @11-57, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @11-57 Identifier { - ident: "#!0_stmt", - }, - ann_type: @11-57 Apply( - "", - "Task", - [ - @11-57 Record { - fields: [], - ext: None, - }, - @11-57 Inferred, - ], - ), - lines_between: [], - body_pattern: @11-57 Identifier { - ident: "#!0_stmt", - }, - body_expr: @11-57 Apply( - @11-57 Var { - module_name: "", - ident: "line", - }, - [ - @11-44 Apply( - @26-36 Var { - module_name: "Str", - ident: "concat", - }, - [ - @11-18 Str( - PlainLine( - "hello", - ), - ), - @37-44 Str( - PlainLine( - "world", - ), - ), - ], - BinOp( - Pizza, - ), - ), - ], - BinOp( - Pizza, - ), - ), - }, - ], - }, - @11-57 Var { - module_name: "", - ident: "#!0_stmt", - }, - ), - @11-57 Closure( - [ - @11-57 Underscore( - "#!stmt", - ), - ], - @63-73 Apply( - @63-70 Var { - module_name: "Task", - ident: "ok", - }, - [ - @71-73 Record( - [], - ), - ], - Space, - ), - ), - ], - BangSuffix, - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap deleted file mode 100644 index e8edbf8406e..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap +++ /dev/null @@ -1,142 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-67, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "copy", - }, - @7-67 Closure( - [ - @8-9 Identifier { - ident: "a", - }, - @10-11 Identifier { - ident: "b", - }, - ], - @19-30 Apply( - @19-30 Var { - module_name: "Task", - ident: "await", - }, - [ - @19-30 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @19-30, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @19-30 Identifier { - ident: "#!1_stmt", - }, - ann_type: @19-30 Apply( - "", - "Task", - [ - @19-30 Record { - fields: [], - ext: None, - }, - @19-30 Inferred, - ], - ), - lines_between: [], - body_pattern: @19-30 Identifier { - ident: "#!1_stmt", - }, - body_expr: @19-30 Apply( - @19-30 Var { - module_name: "", - ident: "line", - }, - [ - @25-30 Str( - PlainLine( - "FOO", - ), - ), - ], - Space, - ), - }, - ], - }, - @19-30 Var { - module_name: "", - ident: "#!1_stmt", - }, - ), - @19-30 Closure( - [ - @19-30 Underscore( - "#!stmt", - ), - ], - @36-67 Apply( - @36-67 Var { - module_name: "", - ident: "mapErr", - }, - [ - @36-48 Apply( - @36-43 Var { - module_name: "CMD", - ident: "new", - }, - [ - @44-48 Str( - PlainLine( - "cp", - ), - ), - ], - Space, - ), - @64-67 Tag( - "ERR", - ), - ], - BinOp( - Pizza, - ), - ), - ), - ], - BangSuffix, - ), - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_suffix_inside_when.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_suffix_inside_when.snap deleted file mode 100644 index b8f0160911b..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_suffix_inside_when.snap +++ /dev/null @@ -1,117 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-154, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @20-30 Apply( - @20-30 Var { - module_name: "Task", - ident: "await", - }, - [ - @20-30 Var { - module_name: "Stdin", - ident: "line", - }, - @20-30 Closure( - [ - @11-17 Identifier { - ident: "result", - }, - ], - @37-154 When( - @42-48 Var { - module_name: "", - ident: "result", - }, - [ - WhenBranch { - patterns: [ - @60-63 Tag( - "End", - ), - ], - value: @79-89 Apply( - @79-86 Var { - module_name: "Task", - ident: "ok", - }, - [ - @87-89 Record( - [], - ), - ], - Space, - ), - guard: None, - }, - WhenBranch { - patterns: [ - @99-109 Apply( - @99-104 Tag( - "Input", - ), - [ - @105-109 Identifier { - ident: "name", - }, - ], - ), - ], - value: @125-154 Apply( - @125-154 Var { - module_name: "Stdout", - ident: "line", - }, - [ - @138-154 Str( - Line( - [ - Plaintext( - "Hello, ", - ), - Interpolated( - @148-152 Var { - module_name: "", - ident: "name", - }, - ), - ], - ), - ), - ], - Space, - ), - guard: None, - }, - ], - ), - ), - ], - BangSuffix, - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap deleted file mode 100644 index 8e3fac63bc5..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap +++ /dev/null @@ -1,115 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-44, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-1 Identifier { - ident: "f", - }, - @4-44 Closure( - [ - @5-6 Identifier { - ident: "x", - }, - ], - @28-29 Apply( - @28-29 Var { - module_name: "Task", - ident: "await", - }, - [ - @14-29 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @28-29, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @14-15 Identifier { - ident: "#!0_expr", - }, - ann_type: @18-19 Apply( - "", - "Task", - [ - @18-19 Apply( - "", - "A", - [], - ), - @18-19 Inferred, - ], - ), - lines_between: [], - body_pattern: @24-25 Identifier { - ident: "#!0_expr", - }, - body_expr: @28-29 Var { - module_name: "", - ident: "x", - }, - }, - ], - }, - @28-29 Var { - module_name: "", - ident: "#!0_expr", - }, - ), - @28-29 Closure( - [ - @24-25 Identifier { - ident: "r", - }, - ], - @35-44 Apply( - @35-42 Var { - module_name: "Task", - ident: "ok", - }, - [ - @43-44 Var { - module_name: "", - ident: "r", - }, - ], - Space, - ), - ), - ], - BangSuffix, - ), - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__var_suffixes.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__var_suffixes.snap deleted file mode 100644 index e2f330e05bd..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__var_suffixes.snap +++ /dev/null @@ -1,107 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-45, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @15-18 Apply( - @15-18 Var { - module_name: "Task", - ident: "await", - }, - [ - @15-18 Var { - module_name: "", - ident: "foo", - }, - @15-18 Closure( - [ - @11-12 Identifier { - ident: "a", - }, - ], - @15-18 Apply( - @15-18 Var { - module_name: "Task", - ident: "await", - }, - [ - @28-33 Var { - module_name: "", - ident: "bar", - }, - @15-18 Closure( - [ - @28-33 Identifier { - ident: "#!0_arg", - }, - ], - @28-33 Apply( - @28-33 Var { - module_name: "Task", - ident: "await", - }, - [ - @28-33 Var { - module_name: "", - ident: "#!0_arg", - }, - @28-33 Closure( - [ - @24-25 Identifier { - ident: "b", - }, - ], - @38-45 Apply( - @38-41 Var { - module_name: "", - ident: "baz", - }, - [ - @42-43 Var { - module_name: "", - ident: "a", - }, - @44-45 Var { - module_name: "", - ident: "b", - }, - ], - Space, - ), - ), - ], - BangSuffix, - ), - ), - ], - BangSuffix, - ), - ), - ], - BangSuffix, - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap deleted file mode 100644 index 3f1ca0ac0e1..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap +++ /dev/null @@ -1,174 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-120, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "list", - }, - @11-120 Apply( - @11-120 Var { - module_name: "Task", - ident: "await", - }, - [ - @16-23 Var { - module_name: "", - ident: "getList", - }, - @11-120 Closure( - [ - @16-23 Identifier { - ident: "#!2_arg", - }, - ], - @11-120 When( - @16-23 Var { - module_name: "", - ident: "#!2_arg", - }, - [ - WhenBranch { - patterns: [ - @36-38 List( - [], - ), - ], - value: @54-65 Apply( - @54-65 Var { - module_name: "Task", - ident: "await", - }, - [ - @54-65 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @54-65, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @54-65 Identifier { - ident: "#!1_stmt", - }, - ann_type: @54-65 Apply( - "", - "Task", - [ - @54-65 Record { - fields: [], - ext: None, - }, - @54-65 Inferred, - ], - ), - lines_between: [], - body_pattern: @54-65 Identifier { - ident: "#!1_stmt", - }, - body_expr: @54-65 Apply( - @54-65 Var { - module_name: "", - ident: "line", - }, - [ - @60-65 Str( - PlainLine( - "foo", - ), - ), - ], - Space, - ), - }, - ], - }, - @54-65 Var { - module_name: "", - ident: "#!1_stmt", - }, - ), - @54-65 Closure( - [ - @54-65 Underscore( - "#!stmt", - ), - ], - @78-89 Apply( - @78-89 Var { - module_name: "", - ident: "line", - }, - [ - @84-89 Str( - PlainLine( - "bar", - ), - ), - ], - Space, - ), - ), - ], - BangSuffix, - ), - guard: None, - }, - WhenBranch { - patterns: [ - @98-99 Underscore( - "", - ), - ], - value: @115-120 Apply( - @115-117 Var { - module_name: "", - ident: "ok", - }, - [ - @118-120 Record( - [], - ), - ], - Space, - ), - guard: None, - }, - ], - ), - ), - ], - BangSuffix, - ), - ), - ], -} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_simple.snap deleted file mode 100644 index d3ef8f9e9c4..00000000000 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_simple.snap +++ /dev/null @@ -1,84 +0,0 @@ ---- -source: crates/compiler/can/tests/test_suffixed.rs -expression: snapshot -snapshot_kind: text ---- -Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-74, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 1 }, - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "list", - }, - @11-74 Apply( - @11-74 Var { - module_name: "Task", - ident: "await", - }, - [ - @16-23 Var { - module_name: "", - ident: "getList", - }, - @11-74 Closure( - [ - @16-23 Identifier { - ident: "#!0_arg", - }, - ], - @11-74 When( - @16-23 Var { - module_name: "", - ident: "#!0_arg", - }, - [ - WhenBranch { - patterns: [ - @36-38 List( - [], - ), - ], - value: @42-49 Str( - PlainLine( - "empty", - ), - ), - guard: None, - }, - WhenBranch { - patterns: [ - @58-59 Underscore( - "", - ), - ], - value: @63-74 Str( - PlainLine( - "non-empty", - ), - ), - guard: None, - }, - ], - ), - ), - ], - BangSuffix, - ), - ), - ], -} diff --git a/crates/compiler/can/tests/test_suffixed.rs b/crates/compiler/can/tests/test_suffixed.rs deleted file mode 100644 index 0740ed66b13..00000000000 --- a/crates/compiler/can/tests/test_suffixed.rs +++ /dev/null @@ -1,690 +0,0 @@ -#[macro_use] -extern crate indoc; - -#[cfg(test)] -mod suffixed_tests { - use bumpalo::Bump; - use insta::assert_snapshot; - use roc_can::desugar::desugar_defs_node_values; - use roc_can::env::{Env, FxMode}; - use roc_can::scope::Scope; - use roc_module::symbol::{IdentIds, ModuleIds, PackageModuleIds}; - use roc_parse::test_helpers::parse_defs_with; - use std::path::Path; - - macro_rules! run_test { - ($src:expr) => {{ - let arena = &Bump::new(); - let home = ModuleIds::default().get_or_insert(&"Test".into()); - - let mut scope = Scope::new( - home, - "TestPath".into(), - IdentIds::default(), - Default::default(), - ); - - let dep_idents = IdentIds::exposed_builtins(0); - let qualified_module_ids = PackageModuleIds::default(); - let mut env = Env::new( - arena, - $src, - home, - Path::new("test.roc"), - &dep_idents, - &qualified_module_ids, - None, - FxMode::Task, - ); - - let mut defs = parse_defs_with(arena, indoc!($src)).unwrap(); - desugar_defs_node_values(&mut env, &mut scope, &mut defs, true); - - let snapshot = format!("{:#?}", &defs); - println!("{}", snapshot); - assert_snapshot!(snapshot); - }}; - } - - /** - * This example tests a suffixed statement, followed - * by a Body with an empty record pattern. - * - * The def final expression is explicitly provided. - */ - #[test] - fn multi_defs_stmts() { - run_test!( - r#" - main = - line! "Ahoy" - {} = "There" |> Stdout.line! - - Task.ok {} - "# - ); - } - - /** - * The most simple suffixed example. A single statement - * without arguments and a final expression. - */ - #[test] - - fn basic() { - run_test!( - r#" - main = - foo! - - ok {} - "# - ); - } - - /** - * A single suffixed statement with arguments applied. - * Note there is no final expression. - */ - #[test] - fn last_suffixed_single() { - run_test!( - r#" - main = foo! "bar" {} "baz" - "# - ); - } - - /** - * Multiple suffixed statements with no - * arguments, and no final expression. - */ - #[test] - fn last_suffixed_multiple() { - run_test!( - r#" - main = - foo! - bar! - baz! - "# - ); - } - - /** - * A definition with a closure that contains a Defs node, which also - * contains a suffixed binops statement. - */ - #[test] - fn closure_simple() { - run_test!( - r#" - main = - x = \msg -> - msg |> line! - ok {} - - x "hi" - "# - ); - } - - /** - * Example of unwrapping a pipline statement - * - * Note pipelines are desugared into Apply functions, - * however this also tests the parser. - * - */ - #[test] - fn simple_pizza() { - run_test!( - r#" - main = - "hello" - |> Str.concat "world" - |> line! - - Task.ok {} - "# - ); - } - - /** - * Example of unwrapping a Result with ?? operator - * - * Note that ?? is desugared into a when expression, - * however this also tests the parser. - * - */ - #[test] - fn simple_double_question() { - run_test!( - r#" - main = - "123" - |> Str.toU8 ?? 255 - |> Num.to_str - |> line! - - Task.ok {} - "# - ); - } - - /** - * Example with a parens suffixed sub-expression - * in the function part of an Apply. - * - * Note how the parens unwraps into an intermediate answer #!0_arg instead of - * unwrapping the def `do`. - * - */ - #[test] - fn body_parens_apply() { - run_test!( - r#" - main = - do = (sayMultiple!) "hi" - do - "# - ); - } - - /** - * Example of unwrapping mixed Body defs with - * Var's of both single and multiple suffixes - */ - #[test] - fn var_suffixes() { - run_test!( - r#" - main = - a = foo! - b = bar!! - baz a b - "# - ); - } - - /** - * Example with a multiple suffixed Var - * - * Note it unwraps into an intermediate answer `#!0_arg` - * - */ - #[test] - fn multiple_suffix() { - run_test!( - r#" - main = - foo!! - bar - "# - ); - } - - /** - * A suffixed expression in the function part of the Apply - */ - #[test] - fn apply_function_suffixed() { - run_test!( - r#" - main = - x = (foo! "bar") "hello" - baz x - "# - ); - } - - /** - * A suffixed expression in an Apply argument position. - */ - #[test] - fn apply_argument_suffixed() { - run_test!( - r#" - main = - x = bar (foo! "hello") - baz x - "# - ); - } - - /** - * Example where the suffixed def is not the first def - */ - #[test] - fn multiple_def_first_suffixed() { - run_test!( - r#" - main = - msg = "hello" - x = foo! msg - bar x - "# - ); - } - - /** - * Annotated defs and a suffixed expression - * with annotations inside a closure - */ - - #[test] - fn closure_with_annotations() { - run_test!( - r#" - main = - x : Str -> Task _ _ - x = \msg -> - - y : Task {} _ - y = line! msg - y - - x "foo" - "# - ); - } - - /** - * Nested suffixed expressions - */ - #[test] - fn nested_simple() { - run_test!( - r#" - run = line! (nextMsg!) - "# - ); - } - - /** - * Nested suffixed expressions - */ - #[test] - fn nested_complex() { - run_test!( - r#" - main = - z = foo! (bar! baz) (blah stuff) - doSomething z - "# - ); - } - - /** - * A closure that contains a Defs node - */ - #[test] - fn closure_with_defs() { - run_test!( - r#" - main = - - foo : Str, {}, Str -> Task {} I32 - foo = \a, _, b -> - line! a - line! b - - Task.ok {} - - foo "bar" {} "baz" - "# - ); - } - - /** - * Test when the suffixed def being unwrapped is not the first or last - */ - #[test] - fn defs_suffixed_middle() { - run_test!( - r#" - main = - a = "Foo" - Stdout.line! a - - printBar! - - printBar = - b = "Bar" - Stdout.line b - "# - ); - } - - /** - * A simple if-then-else statement which is split - */ - #[test] - fn if_simple() { - run_test!( - r#" - main = - isTrue = Task.ok Bool.true - isFalse = Task.ok Bool.false - - if isFalse! then - line "fail" - else if isTrue! then - line "success" - else - line "fail" - "# - ); - } - - /** - * A more complex example including the use of nested Defs nodes - */ - #[test] - fn if_complex() { - run_test!( - r#" - main = - isTrue = Task.ok Bool.true - isFalsey = \x -> Task.ok x - msg : Task {} I32 - msg = - if !(isTrue!) then - line! "fail" - err 1 - else if (isFalsey! Bool.false) then - line! "nope" - ok {} - else - line! "success" - - msg - "# - ); - } - - /** - * Unwrap a trailing binops - */ - #[test] - fn trailing_binops() { - run_test!( - r#" - copy = \a,b -> - line! "FOO" - - CMD.new "cp" - |> mapErr! ERR - "# - ); - } - - /** - * Unwrap a when expression - */ - #[test] - fn when_simple() { - run_test!( - r#" - list = - when getList! is - [] -> "empty" - _ -> "non-empty" - "# - ); - } - - /** - * Unwrap a when expression - */ - #[test] - fn when_branches() { - run_test!( - r#" - list = - when getList! is - [] -> - line! "foo" - line! "bar" - _ -> - ok {} - "# - ); - } - - #[test] - fn trailing_suffix_inside_when() { - run_test!( - r#" - main = - result = Stdin.line! - - when result is - End -> - Task.ok {} - - Input name -> - Stdout.line! "Hello, $(name)" - "# - ); - } - - #[test] - fn dbg_simple() { - run_test!( - r#" - main = - foo = getFoo! - dbg foo - bar! foo - "# - ); - } - - #[test] - fn dbg_expr() { - run_test!( - r#" - main = - dbg (dbg (1 + 1)) - "# - ); - } - - #[test] - fn pizza_dbg() { - run_test!( - r#" - main = - 1 - |> dbg - |> Num.add 2 - |> dbg - "# - ) - } - - #[test] - fn apply_argument_single() { - run_test!( - r#" - main = - c = b a! - c - "# - ); - } - - #[test] - fn apply_argument_multiple() { - run_test!( - r#" - main = - c = b a! x! - c - "# - ); - } - - #[test] - fn bang_in_pipe_root() { - run_test!( - r#" - main = - c = a! |> b - c - "# - ); - } - - #[test] - fn expect_then_bang() { - run_test!( - r#" - main = - expect 1 == 2 - x! - "# - ); - } - - #[test] - fn deep_when() { - run_test!( - r#" - main = - when a is - 0 -> - when b is - 1 -> - c! - "# - ); - } - - #[test] - fn deps_final_expr() { - run_test!( - r#" - main = - when x is - A -> - y = 42 - - if a then - b! - else - c! - B -> - d! - "# - ); - } - - #[test] - fn dbg_stmt_arg() { - run_test!( - r#" - main = - dbg a! - - b - "# - ) - } - - #[test] - fn last_stmt_not_top_level_suffixed() { - run_test!( - r#" - main = - x = 42 - a b! - "# - ); - } - - #[test] - fn nested_defs() { - run_test!( - r##" - main = - x = - a = b! - c! a - - x - "## - ); - } - - #[test] - fn type_annotation() { - run_test!( - r##" - f = \x -> - r : A - r = x! - Task.ok r - "## - ); - } - - #[test] - fn issue_7081() { - run_test!( - r##" - inc = \i -> - if i > 2 then - Err MaxReached - else - Ok (i + 1) - - expect - run = \i -> - newi = - i - |> inc? - |> inc? - Ok newi - result = run 0 - result == Ok 2 - - main = - Stdout.line! "Hello world" - "## - ); - } - - #[test] - fn issue_7103() { - run_test!( - r##" - run : Task {} _ - run = line! "foo" - - main = run - "## - ); - } -} - -#[cfg(test)] -mod test_suffixed_helpers { - - use roc_can::suffixed::is_matching_intermediate_answer; - use roc_parse::ast::Expr; - use roc_parse::ast::Pattern; - use roc_region::all::Loc; - - #[test] - fn test_matching_answer() { - let loc_pat = Loc::at_zero(Pattern::Identifier { ident: "#!0_arg" }); - let loc_new = Loc::at_zero(Expr::Var { - module_name: "", - ident: "#!0_arg", - }); - - std::assert!(is_matching_intermediate_answer(&loc_pat, &loc_new)); - } -} diff --git a/crates/compiler/fmt/src/def.rs b/crates/compiler/fmt/src/def.rs index 1eb30539f38..f5c76d5d7cd 100644 --- a/crates/compiler/fmt/src/def.rs +++ b/crates/compiler/fmt/src/def.rs @@ -1169,7 +1169,7 @@ pub fn starts_with_block_string_literal(expr: &Expr<'_>) -> bool { } Expr::Apply(inner, _, _) => starts_with_block_string_literal(&inner.value), Expr::PncApply(inner, _) => starts_with_block_string_literal(&inner.value), - Expr::TrySuffix { target: _, expr } => starts_with_block_string_literal(expr), + Expr::TrySuffix(inner) => starts_with_block_string_literal(inner), _ => false, } } diff --git a/crates/compiler/fmt/src/expr.rs b/crates/compiler/fmt/src/expr.rs index 8b4d724d716..0e1101fab0f 100644 --- a/crates/compiler/fmt/src/expr.rs +++ b/crates/compiler/fmt/src/expr.rs @@ -14,7 +14,7 @@ use bumpalo::Bump; use roc_module::called_via::{self, BinOp, UnaryOp}; use roc_parse::ast::{ AssignedField, Base, Collection, CommentOrNewline, Expr, ExtractSpaces, Pattern, Spaceable, - Spaces, SpacesAfter, SpacesBefore, TryTarget, WhenBranch, + Spaces, SpacesAfter, SpacesBefore, WhenBranch, }; use roc_parse::ast::{StrLiteral, StrSegment}; use roc_parse::expr::merge_spaces; @@ -349,12 +349,9 @@ fn format_expr_only( buf.push('.'); buf.push_str(key); } - Expr::TrySuffix { expr, target } => { + Expr::TrySuffix(expr) => { expr.format_with_options(buf, Parens::InApply, Newlines::Yes, indent); - match target { - TryTarget::Task => buf.push('!'), - TryTarget::Result => buf.push('?'), - } + buf.push('?'); } Expr::MalformedIdent(str, _) => { buf.indent(indent); @@ -364,10 +361,6 @@ fn format_expr_only( buf.push_str(str); } } - Expr::MalformedSuffixed(loc_expr) => { - buf.indent(indent); - loc_expr.format_with_options(buf, parens, newlines, indent); - } Expr::PrecedenceConflict { .. } => {} Expr::EmptyRecordBuilder { .. } => {} Expr::SingleFieldRecordBuilder { .. } => {} @@ -492,8 +485,6 @@ pub fn expr_is_multiline(me: &Expr<'_>, comments_only: bool) -> bool { } } - Expr::MalformedSuffixed(loc_expr) => expr_is_multiline(&loc_expr.value, comments_only), - // These expressions never have newlines Expr::Float(..) | Expr::Num(..) @@ -513,9 +504,9 @@ pub fn expr_is_multiline(me: &Expr<'_>, comments_only: bool) -> bool { unreachable!("LowLevelTry should only exist after desugaring, not during formatting") } - Expr::RecordAccess(inner, _) - | Expr::TupleAccess(inner, _) - | Expr::TrySuffix { expr: inner, .. } => expr_is_multiline(inner, comments_only), + Expr::RecordAccess(inner, _) | Expr::TupleAccess(inner, _) | Expr::TrySuffix(inner) => { + expr_is_multiline(inner, comments_only) + } // These expressions always have newlines Expr::Defs(_, _) | Expr::When(_, _) => true, @@ -647,7 +638,7 @@ fn requires_space_after_unary(item: &Expr<'_>) -> bool { requires_space_after_unary(inner) } Expr::Apply(inner, _, _) => requires_space_after_unary(&inner.value), - Expr::TrySuffix { target: _, expr } => requires_space_after_unary(expr), + Expr::TrySuffix(expr) => requires_space_after_unary(expr), Expr::SpaceAfter(inner, _) | Expr::SpaceBefore(inner, _) => { requires_space_after_unary(inner) } @@ -1305,15 +1296,12 @@ pub fn expr_lift_spaces<'a, 'b: 'a>( after: &[], }, - Expr::TrySuffix { target, expr } => { + Expr::TrySuffix(expr) => { let expr_lifted = expr_lift_spaces_after(Parens::InApply, arena, expr); Spaces { before: &[], - item: Expr::TrySuffix { - target: *target, - expr: arena.alloc(expr_lifted.item), - }, + item: Expr::TrySuffix(arena.alloc(expr_lifted.item)), after: expr_lifted.after, } } @@ -1380,7 +1368,6 @@ pub fn expr_lift_spaces<'a, 'b: 'a>( } Expr::MalformedIdent(_, _) - | Expr::MalformedSuffixed(_) | Expr::PrecedenceConflict(_) | Expr::EmptyRecordBuilder(_) | Expr::SingleFieldRecordBuilder(_) diff --git a/crates/compiler/load/build.rs b/crates/compiler/load/build.rs index 9a515109271..04b0fc3a58c 100644 --- a/crates/compiler/load/build.rs +++ b/crates/compiler/load/build.rs @@ -25,7 +25,6 @@ const MODULES: &[(ModuleId, &str)] = &[ (ModuleId::DECODE, "Decode.roc"), (ModuleId::HASH, "Hash.roc"), (ModuleId::INSPECT, "Inspect.roc"), - (ModuleId::TASK, "Task.roc"), ]; fn main() { diff --git a/crates/compiler/load/src/lib.rs b/crates/compiler/load/src/lib.rs index 179a7d1da5d..979ff6e3538 100644 --- a/crates/compiler/load/src/lib.rs +++ b/crates/compiler/load/src/lib.rs @@ -256,7 +256,6 @@ fn read_cached_types() -> MutMap { let mod_decode = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Decode.dat")); let mod_hash = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Hash.dat")); let mod_inspect = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Inspect.dat")); - let mod_task = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Task.dat")); let mut output = MutMap::default(); @@ -280,8 +279,6 @@ fn read_cached_types() -> MutMap { output.insert(ModuleId::HASH, deserialize_help(mod_hash)); output.insert(ModuleId::INSPECT, deserialize_help(mod_inspect)); - - output.insert(ModuleId::TASK, deserialize_help(mod_task)); } output diff --git a/crates/compiler/load/tests/helpers/mod.rs b/crates/compiler/load/tests/helpers/mod.rs index 89a3c6236eb..393d9b5b6c1 100644 --- a/crates/compiler/load/tests/helpers/mod.rs +++ b/crates/compiler/load/tests/helpers/mod.rs @@ -178,7 +178,6 @@ pub fn can_expr_with<'a>( &dep_idents, &module_ids, None, - roc_can::env::FxMode::PurityInference, ); // Desugar operators (convert them to Apply calls, taking into account @@ -206,7 +205,6 @@ pub fn can_expr_with<'a>( &dep_idents, &module_ids, None, - roc_can::env::FxMode::PurityInference, ); let (loc_expr, output) = canonicalize_expr( &mut env, diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index bbc4d2e335a..472aac6243c 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -3798,9 +3798,9 @@ mod test_reporting { of these? Set - Task List Dict + Hash ── SYNTAX PROBLEM in /code/proj/Main.roc ─────────────────────────────────────── @@ -4327,26 +4327,33 @@ mod test_reporting { " ), @r#" - ── STATEMENT AFTER EXPRESSION in /code/proj/Main.roc ─────────────────────────── - - I just finished parsing an expression with a series of definitions, + ── IGNORED RESULT in /code/proj/Main.roc ─────────────────────────────────────── - and this line is indented as if it's intended to be part of that - expression: + The result of this expression is ignored: 6│ x == 5 ^^^^^^ - However, I already saw the final expression in that series of - definitions. + Standalone statements are required to produce an empty record, but the + type of this one is: - Tip: An expression like `4`, `"hello"`, or `function_call(MyThing)` is - like `return 4` in other programming languages. To me, it seems like - you did `return 4` followed by more code in the lines after, that code - would never be executed! + Bool - Tip: If you are working with `Task`, this error can happen if you - forgot a `!` somewhere. + If you still want to ignore it, assign it to `_`, like this: + + _ = File.delete! "data.json" + + ── LEFTOVER STATEMENT in /code/proj/Main.roc ─────────────────────────────────── + + This statement does not produce any effects: + + 6│ x == 5 + ^^^^^^ + + Standalone statements are only useful if they call effectful + functions. + + Did you forget to use its result? If not, feel free to remove it. "# ); @@ -6352,7 +6359,7 @@ All branches in an `if` must have the same type! indoc!( r#" platform "folkertdev/foo" - requires { main : Task {} [] } + requires { main! : {} => Result {} [] } exposes [] packages {} imports [] @@ -6372,13 +6379,13 @@ All branches in an `if` must have the same type! I am partway through parsing a header, but I got stuck here: 1│ platform "folkertdev/foo" - 2│ requires { main : Task {} [] } + 2│ requires { main! : {} => Result {} [] } ^ I am expecting a list of type names like `{}` or `{ Model }` next. A full `requires` definition looks like - requires { Model, Msg } {main : Task {} []} + requires { Model, Msg } { main! : {} => Result {} [] } "# ), ) diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index 51706d12b57..ec50636f067 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -15,7 +15,6 @@ use parking_lot::Mutex; use roc_builtins::roc::module_source; use roc_can::abilities::{AbilitiesStore, PendingAbilitiesStore, ResolvedImpl}; use roc_can::constraint::{Constraint as ConstraintSoa, Constraints, TypeOrVar}; -use roc_can::env::FxMode; use roc_can::expr::{Declarations, ExpectLookup, PendingDerives}; use roc_can::module::{ canonicalize_module_defs, ExposedByModule, ExposedForModule, ExposedModuleTypes, Module, @@ -34,7 +33,7 @@ use roc_debug_flags::{ use roc_derive::SharedDerivedModule; use roc_error_macros::internal_error; use roc_late_solve::{AbilitiesView, WorldAbilities}; -use roc_module::ident::{Ident, IdentSuffix, ModuleName, QualifiedModuleName}; +use roc_module::ident::{Ident, ModuleName, QualifiedModuleName}; use roc_module::symbol::{ IdentIds, IdentIdsByModule, Interns, ModuleId, ModuleIds, PQModuleName, PackageModuleIds, PackageQualified, Symbol, @@ -319,7 +318,6 @@ fn start_phase<'a>( exposed_module_ids: state.exposed_modules, exec_mode: state.exec_mode, imported_module_params, - fx_mode: state.fx_mode, } } @@ -714,7 +712,6 @@ struct State<'a> { pub platform_path: PlatformPath<'a>, pub target: Target, pub(self) function_kind: FunctionKind, - pub fx_mode: FxMode, /// Note: only packages and platforms actually expose any modules; /// for all others, this will be empty. @@ -802,7 +799,6 @@ impl<'a> State<'a> { cache_dir, target, function_kind, - fx_mode: FxMode::Task, platform_data: None, platform_path: PlatformPath::NotSpecified, module_cache: ModuleCache::default(), @@ -905,7 +901,6 @@ enum BuildTask<'a> { skip_constraint_gen: bool, exec_mode: ExecutionMode, imported_module_params: VecMap, - fx_mode: FxMode, }, Solve { module: Module, @@ -2274,7 +2269,7 @@ fn update<'a>( config_shorthand, provides, exposes_ids, - requires, + requires: _, .. } => { work.extend(state.dependencies.notify_package(config_shorthand)); @@ -2311,31 +2306,12 @@ fn update<'a>( if header.is_root_module { state.exposed_modules = exposes_ids; } - - if requires.iter().any(|requires| { - IdentSuffix::from_name(requires.value.ident.value).is_bang() - }) { - state.fx_mode = FxMode::PurityInference; - } - } - Builtin { .. } => { - if header.is_root_module { - debug_assert!(matches!(state.platform_path, PlatformPath::NotSpecified)); - state.platform_path = PlatformPath::RootIsModule; - } } - Hosted { exposes, .. } | Module { exposes, .. } => { + Builtin { .. } | Hosted { .. } | Module { .. } => { if header.is_root_module { debug_assert!(matches!(state.platform_path, PlatformPath::NotSpecified)); state.platform_path = PlatformPath::RootIsHosted; } - // WARNING: This will be bypassed if we export a record of effectful functions. This is a temporary hacky method - if exposes - .iter() - .any(|exposed| exposed.value.is_effectful_fn()) - { - state.fx_mode = FxMode::PurityInference; - } } } @@ -2390,7 +2366,6 @@ fn update<'a>( extend_module_with_builtin_import(parsed, ModuleId::DECODE); extend_module_with_builtin_import(parsed, ModuleId::HASH); extend_module_with_builtin_import(parsed, ModuleId::INSPECT); - extend_module_with_builtin_import(parsed, ModuleId::TASK); } state .module_cache @@ -3711,7 +3686,6 @@ fn load_module<'a>( "Decode", ModuleId::DECODE "Hash", ModuleId::HASH "Inspect", ModuleId::INSPECT - "Task", ModuleId::TASK } let (filename, opt_shorthand) = module_name_to_path(src_dir, &module_name, arc_shorthands); @@ -5121,7 +5095,6 @@ fn canonicalize_and_constrain<'a>( exposed_module_ids: &[ModuleId], exec_mode: ExecutionMode, imported_module_params: VecMap, - fx_mode: FxMode, ) -> CanAndCon { let canonicalize_start = Instant::now(); @@ -5148,14 +5121,6 @@ fn canonicalize_and_constrain<'a>( let mut var_store = VarStore::default(); - let fx_mode = if module_id.is_builtin() { - // Allow builtins to expose effectful functions - // even if platform is `Task`-based - FxMode::PurityInference - } else { - fx_mode - }; - let mut module_output = canonicalize_module_defs( arena, parsed_defs, @@ -5173,7 +5138,6 @@ fn canonicalize_and_constrain<'a>( &symbols_from_requires, &mut var_store, opt_shorthand, - fx_mode, ); let mut types = Types::new(); @@ -5285,7 +5249,6 @@ fn canonicalize_and_constrain<'a>( | ModuleId::SET | ModuleId::HASH | ModuleId::INSPECT - | ModuleId::TASK ); if !name.is_builtin() || should_include_builtin { @@ -6285,7 +6248,6 @@ fn run_task<'a>( exposed_module_ids, exec_mode, imported_module_params, - fx_mode, } => { let can_and_con = canonicalize_and_constrain( arena, @@ -6299,7 +6261,6 @@ fn run_task<'a>( exposed_module_ids, exec_mode, imported_module_params, - fx_mode, ); Ok(Msg::CanonicalizedAndConstrained(can_and_con)) diff --git a/crates/compiler/load_internal/src/lib.rs b/crates/compiler/load_internal/src/lib.rs index a19a0a445f5..ef7860a54ae 100644 --- a/crates/compiler/load_internal/src/lib.rs +++ b/crates/compiler/load_internal/src/lib.rs @@ -25,5 +25,4 @@ pub const BUILTIN_MODULES: &[(ModuleId, &str)] = &[ (ModuleId::DECODE, "Decode"), (ModuleId::HASH, "Hash"), (ModuleId::INSPECT, "Inspect"), - (ModuleId::TASK, "Task"), ]; diff --git a/crates/compiler/load_internal/src/module_cache.rs b/crates/compiler/load_internal/src/module_cache.rs index e53c4ee5cc3..73e81f1dd86 100644 --- a/crates/compiler/load_internal/src/module_cache.rs +++ b/crates/compiler/load_internal/src/module_cache.rs @@ -93,7 +93,6 @@ impl Default for ModuleCache<'_> { DECODE, HASH, INSPECT, - TASK, } Self { diff --git a/crates/compiler/module/src/called_via.rs b/crates/compiler/module/src/called_via.rs index 673e236187c..0fac0c3f7c4 100644 --- a/crates/compiler/module/src/called_via.rs +++ b/crates/compiler/module/src/called_via.rs @@ -80,21 +80,17 @@ pub enum CalledVia { /// This call is the result of desugaring a map2-based Record Builder field. e.g. /// ```roc - /// { Task.parallel <- - /// foo: get "a", - /// bar: get "b", + /// { Result.parallel <- + /// foo: get("a"), + /// bar: get("b"), /// } /// ``` /// is transformed into /// ```roc - /// Task.parallel (get "a") (get "b") \foo, bar -> { foo, bar } + /// Result.parallel(get("a"), get("b"), (\foo, bar -> { foo, bar })) /// ``` RecordBuilder, - /// This call is the result of desugaring a Task.await from `!` syntax - /// e.g. Stdout.line! "Hello" becomes Task.await (Stdout.line "Hello") \{} -> ... - BangSuffix, - /// This call is the result of desugaring a Result.try from `?` syntax /// e.g. Dict.get? items "key" becomes Result.try (Dict.get items "key") \item -> ... QuestionSuffix, diff --git a/crates/compiler/module/src/ident.rs b/crates/compiler/module/src/ident.rs index 7571c0f57f8..1b814f8e10e 100644 --- a/crates/compiler/module/src/ident.rs +++ b/crates/compiler/module/src/ident.rs @@ -130,7 +130,6 @@ impl ModuleName { pub const DECODE: &'static str = "Decode"; pub const HASH: &'static str = "Hash"; pub const INSPECT: &'static str = "Inspect"; - pub const TASK: &'static str = "Task"; pub fn as_str(&self) -> &str { self.0.as_str() diff --git a/crates/compiler/module/src/symbol.rs b/crates/compiler/module/src/symbol.rs index 4965eaf75e6..5b99770ac7d 100644 --- a/crates/compiler/module/src/symbol.rs +++ b/crates/compiler/module/src/symbol.rs @@ -1717,24 +1717,6 @@ define_builtins! { 32 INSPECT_TO_INSPECTOR: "to_inspector" 33 INSPECT_TO_STR: "to_str" } - 15 TASK: "Task" => { - 0 TASK_TASK: "Task" exposed_type=true // the Task.Task opaque type - 1 TASK_FOREVER: "forever" - 2 TASK_LOOP: "loop" - 3 TASK_OK: "ok" - 4 TASK_ERR: "err" - 5 TASK_ATTEMPT: "attempt" - 6 TASK_AWAIT: "await" - 7 TASK_ON_ERR: "on_err" - 8 TASK_MAP: "map" - 9 TASK_MAP_ERR: "map_err" - 10 TASK_FROM_RESULT: "from_result" - 11 TASK_BATCH: "batch" - 12 TASK_COMBINE: "combine" - 13 TASK_SEQUENCE: "sequence" - 14 TASK_FOR_EACH: "for_each" - 15 TASK_RESULT: "result" - } - - num_modules: 16 // Keep this count up to date by hand! (TODO: see the mut_map! macro for how we could determine this count correctly in the macro) + + num_modules: 15 // Keep this count up to date by hand! (TODO: see the mut_map! macro for how we could determine this count correctly in the macro) } diff --git a/crates/compiler/parse/src/ast.rs b/crates/compiler/parse/src/ast.rs index affae1acae7..5955a08d231 100644 --- a/crates/compiler/parse/src/ast.rs +++ b/crates/compiler/parse/src/ast.rs @@ -432,16 +432,6 @@ pub enum StrLiteral<'a> { Block(&'a [&'a [StrSegment<'a>]]), } -/// Values that can be tried, extracting success values or "returning early" on failure -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum TryTarget { - // TODO: Remove when purity inference replaces Task fully - /// Tasks suffixed with ! are `Task.await`ed - Task, - /// Results suffixed with ? are `Result.try`ed - Result, -} - #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ResultTryKind { KeywordPrefix, @@ -485,11 +475,8 @@ pub enum Expr<'a> { /// Look up exactly one field on a tuple, e.g. `(x, y).1`. TupleAccess(&'a Expr<'a>, &'a str), - /// Early return on failures - e.g. the ! in `File.readUtf8! path` - TrySuffix { - target: TryTarget, - expr: &'a Expr<'a>, - }, + /// Early return on failures - e.g. the ? in `File.read_utf8(path)?` + TrySuffix(&'a Expr<'a>), // Collection Literals List(Collection<'a, &'a Loc>>), @@ -504,9 +491,9 @@ pub enum Expr<'a> { Tuple(Collection<'a, &'a Loc>>), /// Mapper-based record builders, e.g. - /// { Task.parallel <- - /// foo: Task.getData Foo, - /// bar: Task.getData Bar, + /// { Result.parallel <- + /// foo: Http.get_data(Foo), + /// bar: Http.get_data(Bar), /// } RecordBuilder { mapper: &'a Loc>, @@ -515,7 +502,7 @@ pub enum Expr<'a> { // Lookups Var { - module_name: &'a str, // module_name will only be filled if the original Roc code stated something like `5 + SomeModule.myVar`, module_name will be blank if it was `5 + myVar` + module_name: &'a str, // module_name will only be filled if the original Roc code stated something like `5 + SomeModule.my_var`, module_name will be blank if it was `5 + my_var` ident: &'a str, }, @@ -590,7 +577,6 @@ pub enum Expr<'a> { // Problems MalformedIdent(&'a str, crate::ident::BadIdent), - 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. PrecedenceConflict(&'a PrecedenceConflict<'a>), @@ -626,162 +612,6 @@ pub fn split_loc_exprs_around<'a>( (before, after) } -/// Checks if the bang suffix is applied only at the top level of expression -pub fn is_top_level_suffixed(expr: &Expr) -> bool { - // TODO: should we check BinOps with pizza where the last expression is TrySuffix? - match expr { - Expr::TrySuffix { .. } => true, - Expr::Apply(a, _, _) => is_top_level_suffixed(&a.value), - Expr::PncApply(a, _) => is_top_level_suffixed(&a.value), - Expr::SpaceBefore(a, _) => is_top_level_suffixed(a), - Expr::SpaceAfter(a, _) => is_top_level_suffixed(a), - _ => false, - } -} - -/// Check if the bang suffix is applied recursevely in expression -pub fn is_expr_suffixed(expr: &Expr) -> bool { - match expr { - // expression without arguments, `read!` - Expr::Var { .. } => false, - - Expr::TrySuffix { .. } => true, - - // expression with arguments, `line! "Foo"` - Expr::Apply(sub_loc_expr, apply_args, _) => { - let is_function_suffixed = is_expr_suffixed(&sub_loc_expr.value); - let any_args_suffixed = apply_args.iter().any(|arg| is_expr_suffixed(&arg.value)); - - any_args_suffixed || is_function_suffixed - } - - Expr::PncApply(sub_loc_expr, apply_arg_collection) => { - let is_function_suffixed = is_expr_suffixed(&sub_loc_expr.value); - let any_args_suffixed = apply_arg_collection - .iter() - .any(|arg| is_expr_suffixed(&arg.value)); - - any_args_suffixed || is_function_suffixed - } - - // expression in a pipeline, `"hi" |> say!` - Expr::BinOps(firsts, last) => { - firsts - .iter() - .any(|(chain_loc_expr, _)| is_expr_suffixed(&chain_loc_expr.value)) - || is_expr_suffixed(&last.value) - } - - // expression in a if-then-else, `if isOk! then "ok" else doSomething!` - Expr::If { - if_thens, - final_else, - .. - } => { - let any_if_thens_suffixed = if_thens.iter().any(|(if_then, else_expr)| { - is_expr_suffixed(&if_then.value) || is_expr_suffixed(&else_expr.value) - }); - - is_expr_suffixed(&final_else.value) || any_if_thens_suffixed - } - - // expression in parens `(read!)` - Expr::ParensAround(sub_loc_expr) => is_expr_suffixed(sub_loc_expr), - - // expression in a closure - Expr::Closure(_, sub_loc_expr) => is_expr_suffixed(&sub_loc_expr.value), - - // expressions inside a Defs - Expr::Defs(defs, expr) => { - let any_defs_suffixed = defs.tags.iter().any(|tag| match tag.split() { - Ok(_) => false, - Err(value_index) => match defs.value_defs[value_index.index()] { - ValueDef::Body(_, loc_expr) => is_expr_suffixed(&loc_expr.value), - ValueDef::AnnotatedBody { body_expr, .. } => is_expr_suffixed(&body_expr.value), - _ => false, - }, - }); - - any_defs_suffixed || is_expr_suffixed(&expr.value) - } - Expr::Float(_) => false, - Expr::Num(_) => false, - Expr::NonBase10Int { .. } => false, - Expr::Str(_) => false, - Expr::SingleQuote(_) => false, - Expr::RecordAccess(a, _) => is_expr_suffixed(a), - Expr::AccessorFunction(_) => false, - Expr::RecordUpdater(_) => false, - Expr::TupleAccess(a, _) => is_expr_suffixed(a), - Expr::List(items) => items.iter().any(|x| is_expr_suffixed(&x.value)), - Expr::RecordUpdate { update, fields } => { - is_expr_suffixed(&update.value) - || fields - .iter() - .any(|field| is_assigned_value_suffixed(&field.value)) - } - Expr::Record(items) => items - .iter() - .any(|field| is_assigned_value_suffixed(&field.value)), - Expr::Tuple(items) => items.iter().any(|x| is_expr_suffixed(&x.value)), - Expr::RecordBuilder { mapper: _, fields } => fields - .iter() - .any(|field| is_assigned_value_suffixed(&field.value)), - Expr::Underscore(_) => false, - Expr::Crash => false, - Expr::Tag(_) => false, - Expr::OpaqueRef(_) => false, - Expr::Dbg => false, - Expr::DbgStmt { - first, - extra_args, - continuation, - } => { - is_expr_suffixed(&first.value) - || extra_args.iter().any(|a| is_expr_suffixed(&a.value)) - || is_expr_suffixed(&continuation.value) - } - Expr::LowLevelDbg(_, a, b) => is_expr_suffixed(&a.value) || is_expr_suffixed(&b.value), - Expr::Try => false, - Expr::LowLevelTry(loc_expr, _) => is_expr_suffixed(&loc_expr.value), - Expr::UnaryOp(a, _) => is_expr_suffixed(&a.value), - Expr::When(cond, branches) => { - is_expr_suffixed(&cond.value) || branches.iter().any(|x| is_when_branch_suffixed(x)) - } - Expr::Return(a, b) => { - is_expr_suffixed(&a.value) || b.is_some_and(|loc_b| is_expr_suffixed(&loc_b.value)) - } - Expr::SpaceBefore(a, _) => is_expr_suffixed(a), - Expr::SpaceAfter(a, _) => is_expr_suffixed(a), - Expr::MalformedIdent(_, _) => false, - Expr::MalformedSuffixed(_) => false, - Expr::PrecedenceConflict(_) => false, - Expr::EmptyRecordBuilder(_) => false, - Expr::SingleFieldRecordBuilder(_) => false, - Expr::OptionalFieldInRecordBuilder(_, _) => false, - } -} - -fn is_when_branch_suffixed(branch: &WhenBranch<'_>) -> bool { - is_expr_suffixed(&branch.value.value) - || branch - .guard - .map(|x| is_expr_suffixed(&x.value)) - .unwrap_or(false) -} - -fn is_assigned_value_suffixed<'a>(value: &AssignedField<'a, Expr<'a>>) -> bool { - match value { - AssignedField::RequiredValue(_, _, a) - | AssignedField::OptionalValue(_, _, a) - | AssignedField::IgnoredValue(_, _, a) => is_expr_suffixed(&a.value), - AssignedField::LabelOnly(_) => false, - AssignedField::SpaceBefore(a, _) | AssignedField::SpaceAfter(a, _) => { - is_assigned_value_suffixed(a) - } - } -} - pub fn split_around(items: &[T], target: usize) -> (&[T], &[T]) { let (before, rest) = items.split_at(target); let after = &rest[1..]; @@ -1082,7 +912,7 @@ impl<'a, 'b> RecursiveValueDefIter<'a, 'b> { } RecordAccess(expr, _) | TupleAccess(expr, _) - | TrySuffix { expr, .. } + | TrySuffix(expr) | SpaceBefore(expr, _) | SpaceAfter(expr, _) | ParensAround(expr) => expr_stack.push(expr), @@ -1106,8 +936,7 @@ impl<'a, 'b> RecursiveValueDefIter<'a, 'b> { | Tag(_) | OpaqueRef(_) | MalformedIdent(_, _) - | PrecedenceConflict(_) - | MalformedSuffixed(_) => { /* terminal */ } + | PrecedenceConflict(_) => { /* terminal */ } } } } @@ -2572,7 +2401,7 @@ impl<'a> Malformed for Expr<'a> { RecordAccess(inner, _) | TupleAccess(inner, _) | - TrySuffix { expr: inner, .. } => inner.is_malformed(), + TrySuffix(inner) => inner.is_malformed(), List(items) => items.is_malformed(), @@ -2602,7 +2431,6 @@ impl<'a> Malformed for Expr<'a> { ParensAround(expr) => expr.is_malformed(), MalformedIdent(_, _) | - MalformedSuffixed(..) | PrecedenceConflict(_) | EmptyRecordBuilder(_) | SingleFieldRecordBuilder(_) | diff --git a/crates/compiler/parse/src/expr.rs b/crates/compiler/parse/src/expr.rs index c68e0266efd..a9ac3cbe7db 100644 --- a/crates/compiler/parse/src/expr.rs +++ b/crates/compiler/parse/src/expr.rs @@ -1,9 +1,8 @@ use crate::ast::{ - is_expr_suffixed, AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces, - Implements, ImplementsAbilities, ImportAlias, ImportAsKeyword, ImportExposingKeyword, - ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport, - ModuleImportParams, Pattern, Spaceable, Spaced, Spaces, SpacesBefore, TryTarget, - TypeAnnotation, TypeDef, TypeHeader, ValueDef, + AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces, Implements, + ImplementsAbilities, ImportAlias, ImportAsKeyword, ImportExposingKeyword, ImportedModuleName, + IngestedFileAnnotation, IngestedFileImport, ModuleImport, ModuleImportParams, Pattern, + Spaceable, Spaced, Spaces, SpacesBefore, TypeAnnotation, TypeDef, TypeHeader, ValueDef, }; use crate::blankspace::{ loc_space0_e, require_newline_or_eof, space0_after_e, space0_around_ee, space0_before_e, @@ -157,12 +156,7 @@ fn record_field_access_chain<'a>() -> impl Parser<'a, Vec<'a, Suffix<'a>>, EExpr ) ) ), - map(byte(b'!', EExpr::Access), |_| Suffix::TrySuffix( - TryTarget::Task - )), - map(byte(b'?', EExpr::Access), |_| Suffix::TrySuffix( - TryTarget::Result - )), + map(byte(b'?', EExpr::Access), |()| Suffix::TrySuffix) )) } @@ -876,7 +870,6 @@ impl<'a> ExprState<'a> { } else if !self.expr.value.is_tag() && !self.expr.value.is_opaque() && !self.arguments.is_empty() - && !is_expr_suffixed(&self.expr.value) { let region = Region::across_all(self.arguments.iter().map(|v| &v.region)); @@ -2222,7 +2215,6 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result( Suffix::Accessor(Accessor::TupleIndex(field)) => { Expr::TupleAccess(arena.alloc(value), field) } - Suffix::TrySuffix(target) => Expr::TrySuffix { - target, - expr: arena.alloc(value), - }, + Suffix::TrySuffix => Expr::TrySuffix(arena.alloc(value)), }) } diff --git a/crates/compiler/parse/src/header.rs b/crates/compiler/parse/src/header.rs index 4f602a164e1..b885915929f 100644 --- a/crates/compiler/parse/src/header.rs +++ b/crates/compiler/parse/src/header.rs @@ -857,10 +857,10 @@ fn imports_entry<'a>() -> impl Parser<'a, Spaced<'a, ImportsEntry<'a>>, EImports shortname(), byte(b'.', EImports::ShorthandDot) ))), - // e.g. `Task` + // e.g. `List` module_name_help(EImports::ModuleName) ), - // e.g. `.{ Task, after}` + // e.g. `.{ List, after }` optional(skip_first( byte(b'.', EImports::ExposingDot), collection_trailing_sep_e( diff --git a/crates/compiler/parse/src/ident.rs b/crates/compiler/parse/src/ident.rs index 2e8f625f2c7..74b60751256 100644 --- a/crates/compiler/parse/src/ident.rs +++ b/crates/compiler/parse/src/ident.rs @@ -1,4 +1,3 @@ -use crate::ast::TryTarget; use crate::keyword::is_allowed_identifier; use crate::parser::Progress::{self, *}; use crate::parser::{BadInputError, EExpr, ParseResult, Parser}; @@ -378,7 +377,7 @@ impl<'a> Accessor<'a> { #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Suffix<'a> { Accessor(Accessor<'a>), - TrySuffix(TryTarget), + TrySuffix, } /// a `.foo` or `.1` accessor function diff --git a/crates/compiler/parse/src/normalize.rs b/crates/compiler/parse/src/normalize.rs index 3083cac0d71..e95844104ce 100644 --- a/crates/compiler/parse/src/normalize.rs +++ b/crates/compiler/parse/src/normalize.rs @@ -691,10 +691,7 @@ impl<'a> Normalize<'a> for Expr<'a> { Expr::AccessorFunction(a) => Expr::AccessorFunction(a), Expr::RecordUpdater(a) => Expr::RecordUpdater(a), Expr::TupleAccess(a, b) => Expr::TupleAccess(arena.alloc(a.normalize(arena)), b), - Expr::TrySuffix { expr: a, target } => Expr::TrySuffix { - expr: arena.alloc(a.normalize(arena)), - target, - }, + Expr::TrySuffix(a) => Expr::TrySuffix(arena.alloc(a.normalize(arena))), Expr::List(a) => Expr::List(a.normalize(arena)), Expr::RecordUpdate { update, fields } => Expr::RecordUpdate { update: arena.alloc(update.normalize(arena)), @@ -779,7 +776,6 @@ impl<'a> Normalize<'a> for Expr<'a> { a.normalize(arena) } Expr::MalformedIdent(a, b) => Expr::MalformedIdent(a, remove_spaces_bad_ident(b)), - Expr::MalformedSuffixed(a) => Expr::MalformedSuffixed(a), Expr::PrecedenceConflict(a) => Expr::PrecedenceConflict(a), Expr::SpaceBefore(a, _) => a.normalize(arena), Expr::SpaceAfter(a, _) => a.normalize(arena), diff --git a/crates/compiler/problem/src/can.rs b/crates/compiler/problem/src/can.rs index 78dc0720468..c75dbdd2772 100644 --- a/crates/compiler/problem/src/can.rs +++ b/crates/compiler/problem/src/can.rs @@ -251,7 +251,6 @@ pub enum Problem { ReturnAtEndOfFunction { region: Region, }, - StmtAfterExpr(Region), UnsuffixedEffectfulRecordField(Region), SuffixedPureRecordField(Region), EmptyTupleType(Region), @@ -339,7 +338,6 @@ impl Problem { Problem::ReturnOutsideOfFunction { .. } => Warning, Problem::StatementsAfterReturn { .. } => Warning, Problem::ReturnAtEndOfFunction { .. } => Warning, - Problem::StmtAfterExpr(_) => Fatal, Problem::UnsuffixedEffectfulRecordField(_) | Problem::SuffixedPureRecordField(..) => { Warning } @@ -407,53 +405,6 @@ impl Problem { tag_union_region: region, .. } - | Problem::RuntimeError(RuntimeError::Shadowing { - original_region: region, - .. - }) - | Problem::RuntimeError(RuntimeError::InvalidOptionalValue { - record_region: region, - .. - }) - | Problem::RuntimeError(RuntimeError::UnsupportedPattern(region)) - | Problem::RuntimeError(RuntimeError::MalformedPattern(_, region)) - | Problem::RuntimeError(RuntimeError::LookupNotInScope { - loc_name: Loc { region, .. }, - suggestion_options: _, - underscored_suggestion_region: _, - }) - | Problem::RuntimeError(RuntimeError::OpaqueNotDefined { - usage: Loc { region, .. }, - .. - }) - | Problem::RuntimeError(RuntimeError::OpaqueOutsideScope { - referenced_region: region, - .. - }) - | Problem::RuntimeError(RuntimeError::OpaqueNotApplied(Loc { region, .. })) - | Problem::RuntimeError(RuntimeError::OpaqueAppliedToMultipleArgs(region)) - | Problem::RuntimeError(RuntimeError::ValueNotExposed { region, .. }) - | Problem::RuntimeError(RuntimeError::ModuleNotImported { region, .. }) - | Problem::RuntimeError(RuntimeError::InvalidPrecedence(_, region)) - | Problem::RuntimeError(RuntimeError::MalformedIdentifier(_, _, region)) - | Problem::RuntimeError(RuntimeError::MalformedTypeName(_, region)) - | Problem::RuntimeError(RuntimeError::MalformedSuffixed(region)) - | Problem::RuntimeError(RuntimeError::InvalidRecordUpdate { region }) - | Problem::RuntimeError(RuntimeError::InvalidFloat(_, region, _)) - | Problem::RuntimeError(RuntimeError::InvalidInt(_, _, region, _)) - | Problem::RuntimeError(RuntimeError::InvalidInterpolation(region)) - | Problem::RuntimeError(RuntimeError::InvalidHexadecimal(region)) - | Problem::RuntimeError(RuntimeError::InvalidUnicodeCodePt(region)) - | Problem::RuntimeError(RuntimeError::EmptySingleQuote(region)) - | Problem::RuntimeError(RuntimeError::MultipleCharsInSingleQuote(region)) - | Problem::RuntimeError(RuntimeError::DegenerateBranch(region)) - | Problem::RuntimeError(RuntimeError::EmptyRecordBuilder(region)) - | Problem::RuntimeError(RuntimeError::SingleFieldRecordBuilder(region)) - | Problem::RuntimeError(RuntimeError::OptionalFieldInRecordBuilder { - record: _, - field: region, - }) - | Problem::RuntimeError(RuntimeError::ReadIngestedFileError { region, .. }) | Problem::InvalidAliasRigid { region, .. } | Problem::InvalidInterpolation(region) | Problem::EmptyTupleType(region) @@ -512,23 +463,17 @@ impl Problem { | Problem::ReturnAtEndOfFunction { region } | Problem::UnboundTypeVarsInAs(region) | Problem::UnsuffixedEffectfulRecordField(region) - | Problem::SuffixedPureRecordField(region) - | Problem::StmtAfterExpr(region) => Some(*region), + | Problem::SuffixedPureRecordField(region) => Some(*region), - Problem::RuntimeError(RuntimeError::CircularDef(cycle_entries)) - | Problem::BadRecursion(cycle_entries) => { + Problem::BadRecursion(cycle_entries) => { cycle_entries.first().map(|entry| entry.expr_region) } - Problem::RuntimeError(RuntimeError::UnresolvedTypeVar) - | Problem::RuntimeError(RuntimeError::ErroneousType) - | Problem::RuntimeError(RuntimeError::NonExhaustivePattern) - | Problem::RuntimeError(RuntimeError::NoImplementation) - | Problem::RuntimeError(RuntimeError::VoidValue) - | Problem::RuntimeError(RuntimeError::ExposedButNotDefined(_)) - | Problem::RuntimeError(RuntimeError::NoImplementationNamed { .. }) - | Problem::FileProblem { .. } - | Problem::ExposedButNotDefined(_) => None, + Problem::RuntimeError(runtime_error) => { + Some(runtime_error.region()).filter(|region| region.is_empty()) + } + + Problem::FileProblem { .. } | Problem::ExposedButNotDefined(_) => None, } } } @@ -720,7 +665,7 @@ pub enum RuntimeError { field: Region, }, - MalformedSuffixed(Region), + NonFunctionHostedAnnotation(Region), } impl RuntimeError { @@ -753,7 +698,6 @@ impl RuntimeError { | RuntimeError::InvalidPrecedence(_, region) | RuntimeError::MalformedIdentifier(_, _, region) | RuntimeError::MalformedTypeName(_, region) - | RuntimeError::MalformedSuffixed(region) | RuntimeError::InvalidRecordUpdate { region } | RuntimeError::InvalidFloat(_, region, _) | RuntimeError::InvalidInt(_, _, region, _) @@ -769,17 +713,21 @@ impl RuntimeError { field: region, } | RuntimeError::ReadIngestedFileError { region, .. } - | RuntimeError::InvalidUnicodeCodePt(region) => *region, - RuntimeError::UnresolvedTypeVar | RuntimeError::ErroneousType => Region::zero(), + | RuntimeError::InvalidUnicodeCodePt(region) + | RuntimeError::NonFunctionHostedAnnotation(region) => *region, + + RuntimeError::UnresolvedTypeVar + | RuntimeError::ErroneousType + | RuntimeError::NonExhaustivePattern + | RuntimeError::NoImplementationNamed { .. } + | RuntimeError::NoImplementation + | RuntimeError::VoidValue + | RuntimeError::ExposedButNotDefined(_) => Region::zero(), + RuntimeError::LookupNotInScope { loc_name, .. } => loc_name.region, RuntimeError::OpaqueNotDefined { usage, .. } => usage.region, RuntimeError::OpaqueNotApplied(ident) => ident.region, RuntimeError::CircularDef(cycle) => cycle[0].symbol_region, - RuntimeError::NonExhaustivePattern => Region::zero(), - RuntimeError::NoImplementationNamed { .. } - | RuntimeError::NoImplementation - | RuntimeError::VoidValue - | RuntimeError::ExposedButNotDefined(_) => Region::zero(), } } } diff --git a/crates/compiler/test_gen/src/gen_primitives.rs b/crates/compiler/test_gen/src/gen_primitives.rs index 36130badbf4..24e21ca97cc 100644 --- a/crates/compiler/test_gen/src/gen_primitives.rs +++ b/crates/compiler/test_gen/src/gen_primitives.rs @@ -1866,51 +1866,6 @@ fn unified_empty_closure_byte() { ); } -#[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] -fn task_always_twice() { - assert_evals_to!( - indoc!( - r#" - app "test" provides [main] to "./platform" - - Effect a := {} -> a - - effect_always : a -> Effect a - effect_always = \x -> - inner = \{} -> x - - @Effect inner - - effect_after : Effect a, (a -> Effect b) -> Effect b - effect_after = \(@Effect thunk), transform -> transform (thunk {}) - - MyTask a err : Effect (Result a err) - - always : a -> MyTask a * - always = \x -> effect_always (Ok x) - - fail : err -> MyTask * err - fail = \x -> effect_always (Err x) - - after : MyTask a err, (a -> MyTask b err) -> MyTask b err - after = \task, transform -> - effect_after task \res -> - when res is - Ok x -> transform x - Err e -> fail e - - main : MyTask {} F64 - main = after (always "foo") (\_ -> always {}) - - "# - ), - (), - (f64, u8), - |_| () - ); -} - #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn wildcard_rigid() { diff --git a/crates/compiler/test_mono/generated/capture_void_layout_task.txt b/crates/compiler/test_mono/generated/capture_void_layout_task.txt index 27432303ed5..ef79c76147f 100644 --- a/crates/compiler/test_mono/generated/capture_void_layout_task.txt +++ b/crates/compiler/test_mono/generated/capture_void_layout_task.txt @@ -1,4 +1,4 @@ -procedure List.100 (#Derived_gen.13, #Derived_gen.14, #Derived_gen.15, #Derived_gen.16, #Derived_gen.17): +procedure List.100 (#Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14, #Derived_gen.15): joinpoint List.668 List.174 List.175 List.176 List.177 List.178: let List.670 : Int1 = CallByName Num.22 List.177 List.178; if List.670 then @@ -11,8 +11,8 @@ procedure List.100 (#Derived_gen.13, #Derived_gen.14, #Derived_gen.15, #Derived_ dec List.174; ret List.175; in - inc #Derived_gen.13; - jump List.668 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15 #Derived_gen.16 #Derived_gen.17; + inc #Derived_gen.11; + jump List.668 #Derived_gen.11 #Derived_gen.12 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15; procedure List.18 (List.171, List.172, List.173): let List.666 : U64 = 0i64; @@ -38,8 +38,8 @@ procedure Num.51 (#Attr.2, #Attr.3): procedure Test.10 (Test.69, #Attr.12): let Test.72 : {} = UnionAtIndex (Id 0) (Index 0) #Attr.12; - let #Derived_gen.20 : Int1 = lowlevel RefCountIsUnique #Attr.12; - if #Derived_gen.20 then + let #Derived_gen.18 : Int1 = lowlevel RefCountIsUnique #Attr.12; + if #Derived_gen.18 then free #Attr.12; ret Test.72; else @@ -53,7 +53,7 @@ procedure Test.10 (Test.69, #Attr.12): procedure Test.14 (Test.45, #Attr.12): let Test.55 : {{}, []} = UnionAtIndex (Id 1) (Index 1) #Attr.12; let Test.54 : [C {}, C *self {{}, []}] = UnionAtIndex (Id 1) (Index 0) #Attr.12; - joinpoint #Derived_gen.18: + joinpoint #Derived_gen.19: let Test.50 : {} = Struct {}; let Test.51 : U8 = GetTagId Test.54; joinpoint Test.52 Test.15: @@ -80,14 +80,14 @@ procedure Test.14 (Test.45, #Attr.12): jump Test.52 Test.53; in - let #Derived_gen.19 : Int1 = lowlevel RefCountIsUnique #Attr.12; - if #Derived_gen.19 then + let #Derived_gen.20 : Int1 = lowlevel RefCountIsUnique #Attr.12; + if #Derived_gen.20 then free #Attr.12; - jump #Derived_gen.18; + jump #Derived_gen.19; else inc Test.54; decref #Attr.12; - jump #Derived_gen.18; + jump #Derived_gen.19; procedure Test.20 (Test.21, Test.18): let Test.23 : [C {}, C []] = CallByName Test.32 Test.21 Test.18; diff --git a/crates/compiler/test_mono/generated/compose_recursive_lambda_set_productive_nullable_wrapped.txt b/crates/compiler/test_mono/generated/compose_recursive_lambda_set_productive_nullable_wrapped.txt index 67ab79377c7..7afd72ba2c9 100644 --- a/crates/compiler/test_mono/generated/compose_recursive_lambda_set_productive_nullable_wrapped.txt +++ b/crates/compiler/test_mono/generated/compose_recursive_lambda_set_productive_nullable_wrapped.txt @@ -2,7 +2,7 @@ procedure Bool.2 (): let Bool.23 : Int1 = true; ret Bool.23; -procedure List.100 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4): +procedure List.100 (#Derived_gen.3, #Derived_gen.4, #Derived_gen.5, #Derived_gen.6, #Derived_gen.7): joinpoint List.668 List.174 List.175 List.176 List.177 List.178: let List.670 : Int1 = CallByName Num.22 List.177 List.178; if List.670 then @@ -15,8 +15,8 @@ procedure List.100 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen dec List.174; ret List.175; in - inc #Derived_gen.0; - jump List.668 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; + inc #Derived_gen.3; + jump List.668 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5 #Derived_gen.6 #Derived_gen.7; procedure List.18 (List.171, List.172, List.173): let List.666 : U64 = 0i64; @@ -47,11 +47,11 @@ procedure Str.3 (#Attr.2, #Attr.3): procedure Test.1 (Test.5): ret Test.5; -procedure Test.11 (#Derived_gen.8, #Derived_gen.9): +procedure Test.11 (#Derived_gen.10, #Derived_gen.11): joinpoint Test.27 Test.12 #Attr.12: let Test.34 : Int1 = UnionAtIndex (Id 2) (Index 1) #Attr.12; let Test.33 : [, C *self Int1, C *self Int1] = UnionAtIndex (Id 2) (Index 0) #Attr.12; - joinpoint #Derived_gen.12: + joinpoint #Derived_gen.14: joinpoint Test.31 Test.29: let Test.30 : U8 = GetTagId Test.33; switch Test.30: @@ -78,16 +78,16 @@ procedure Test.11 (#Derived_gen.8, #Derived_gen.9): jump Test.31 Test.32; in - let #Derived_gen.13 : Int1 = lowlevel RefCountIsUnique #Attr.12; - if #Derived_gen.13 then + let #Derived_gen.15 : Int1 = lowlevel RefCountIsUnique #Attr.12; + if #Derived_gen.15 then free #Attr.12; - jump #Derived_gen.12; + jump #Derived_gen.14; else inc Test.33; decref #Attr.12; - jump #Derived_gen.12; + jump #Derived_gen.14; in - jump Test.27 #Derived_gen.8 #Derived_gen.9; + jump Test.27 #Derived_gen.10 #Derived_gen.11; procedure Test.2 (Test.13): ret Test.13; @@ -118,7 +118,7 @@ procedure Test.6 (Test.7, Test.8, Test.5): procedure Test.9 (Test.10, #Attr.12): let Test.43 : Int1 = UnionAtIndex (Id 1) (Index 1) #Attr.12; let Test.42 : [, C *self Int1, C *self Int1] = UnionAtIndex (Id 1) (Index 0) #Attr.12; - joinpoint #Derived_gen.14: + joinpoint #Derived_gen.12: let Test.39 : U8 = GetTagId Test.42; joinpoint Test.40 Test.38: switch Test.43: @@ -146,14 +146,14 @@ procedure Test.9 (Test.10, #Attr.12): jump Test.40 Test.41; in - let #Derived_gen.15 : Int1 = lowlevel RefCountIsUnique #Attr.12; - if #Derived_gen.15 then + let #Derived_gen.13 : Int1 = lowlevel RefCountIsUnique #Attr.12; + if #Derived_gen.13 then free #Attr.12; - jump #Derived_gen.14; + jump #Derived_gen.12; else inc Test.42; decref #Attr.12; - jump #Derived_gen.14; + jump #Derived_gen.12; procedure Test.0 (): let Test.45 : Int1 = false; diff --git a/crates/compiler/test_mono/generated/encode_derived_nested_record_string.txt b/crates/compiler/test_mono/generated/encode_derived_nested_record_string.txt index 8ab4a1883e0..31d18ca68de 100644 --- a/crates/compiler/test_mono/generated/encode_derived_nested_record_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_nested_record_string.txt @@ -67,7 +67,7 @@ procedure Encode.26 (Encode.107, Encode.108): let Encode.110 : List U8 = CallByName Encode.24 Encode.111 Encode.112 Encode.108; ret Encode.110; -procedure List.100 (#Derived_gen.29, #Derived_gen.30, #Derived_gen.31, #Derived_gen.32, #Derived_gen.33): +procedure List.100 (#Derived_gen.26, #Derived_gen.27, #Derived_gen.28, #Derived_gen.29, #Derived_gen.30): joinpoint List.694 List.174 List.175 List.176 List.177 List.178: let List.696 : Int1 = CallByName Num.22 List.177 List.178; if List.696 then @@ -81,10 +81,10 @@ procedure List.100 (#Derived_gen.29, #Derived_gen.30, #Derived_gen.31, #Derived_ dec List.174; ret List.175; in - inc #Derived_gen.29; - jump List.694 #Derived_gen.29 #Derived_gen.30 #Derived_gen.31 #Derived_gen.32 #Derived_gen.33; + inc #Derived_gen.26; + jump List.694 #Derived_gen.26 #Derived_gen.27 #Derived_gen.28 #Derived_gen.29 #Derived_gen.30; -procedure List.100 (#Derived_gen.37, #Derived_gen.38, #Derived_gen.39, #Derived_gen.40, #Derived_gen.41): +procedure List.100 (#Derived_gen.34, #Derived_gen.35, #Derived_gen.36, #Derived_gen.37, #Derived_gen.38): joinpoint List.668 List.174 List.175 List.176 List.177 List.178: let List.670 : Int1 = CallByName Num.22 List.177 List.178; if List.670 then @@ -98,8 +98,8 @@ procedure List.100 (#Derived_gen.37, #Derived_gen.38, #Derived_gen.39, #Derived_ dec List.174; ret List.175; in - inc #Derived_gen.37; - jump List.668 #Derived_gen.37 #Derived_gen.38 #Derived_gen.39 #Derived_gen.40 #Derived_gen.41; + inc #Derived_gen.34; + jump List.668 #Derived_gen.34 #Derived_gen.35 #Derived_gen.36 #Derived_gen.37 #Derived_gen.38; procedure List.18 (List.171, List.172, List.173): let List.666 : U64 = 0i64; diff --git a/crates/compiler/test_mono/generated/encode_derived_tag_one_field_string.txt b/crates/compiler/test_mono/generated/encode_derived_tag_one_field_string.txt index 083b483cc49..01194822d9f 100644 --- a/crates/compiler/test_mono/generated/encode_derived_tag_one_field_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_tag_one_field_string.txt @@ -40,7 +40,7 @@ procedure Encode.26 (Encode.107, Encode.108): let Encode.110 : List U8 = CallByName Encode.24 Encode.111 Encode.112 Encode.108; ret Encode.110; -procedure List.100 (#Derived_gen.22, #Derived_gen.23, #Derived_gen.24, #Derived_gen.25, #Derived_gen.26): +procedure List.100 (#Derived_gen.10, #Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14): joinpoint List.668 List.174 List.175 List.176 List.177 List.178: let List.670 : Int1 = CallByName Num.22 List.177 List.178; if List.670 then @@ -54,8 +54,8 @@ procedure List.100 (#Derived_gen.22, #Derived_gen.23, #Derived_gen.24, #Derived_ dec List.174; ret List.175; in - inc #Derived_gen.22; - jump List.668 #Derived_gen.22 #Derived_gen.23 #Derived_gen.24 #Derived_gen.25 #Derived_gen.26; + inc #Derived_gen.10; + jump List.668 #Derived_gen.10 #Derived_gen.11 #Derived_gen.12 #Derived_gen.13 #Derived_gen.14; procedure List.13 (#Attr.2, #Attr.3): let List.691 : List Str = lowlevel ListPrepend #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/encode_derived_tag_two_payloads_string.txt b/crates/compiler/test_mono/generated/encode_derived_tag_two_payloads_string.txt index 91ff1a9c407..9dc0c45f646 100644 --- a/crates/compiler/test_mono/generated/encode_derived_tag_two_payloads_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_tag_two_payloads_string.txt @@ -43,7 +43,7 @@ procedure Encode.26 (Encode.107, Encode.108): let Encode.110 : List U8 = CallByName Encode.24 Encode.111 Encode.112 Encode.108; ret Encode.110; -procedure List.100 (#Derived_gen.23, #Derived_gen.24, #Derived_gen.25, #Derived_gen.26, #Derived_gen.27): +procedure List.100 (#Derived_gen.20, #Derived_gen.21, #Derived_gen.22, #Derived_gen.23, #Derived_gen.24): joinpoint List.668 List.174 List.175 List.176 List.177 List.178: let List.670 : Int1 = CallByName Num.22 List.177 List.178; if List.670 then @@ -57,8 +57,8 @@ procedure List.100 (#Derived_gen.23, #Derived_gen.24, #Derived_gen.25, #Derived_ dec List.174; ret List.175; in - inc #Derived_gen.23; - jump List.668 #Derived_gen.23 #Derived_gen.24 #Derived_gen.25 #Derived_gen.26 #Derived_gen.27; + inc #Derived_gen.20; + jump List.668 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23 #Derived_gen.24; procedure List.13 (#Attr.2, #Attr.3): let List.691 : List Str = lowlevel ListPrepend #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/inline_return_joinpoints_in_union_lambda_set.txt b/crates/compiler/test_mono/generated/inline_return_joinpoints_in_union_lambda_set.txt index cfc60809640..30a62bd1bcf 100644 --- a/crates/compiler/test_mono/generated/inline_return_joinpoints_in_union_lambda_set.txt +++ b/crates/compiler/test_mono/generated/inline_return_joinpoints_in_union_lambda_set.txt @@ -17,7 +17,7 @@ procedure Test.4 (Test.5, #Attr.12): let Test.16 : I64 = CallByName Num.19 Test.5 Test.17; ret Test.16; -procedure Test.0 (#Derived_gen.2): +procedure Test.0 (#Derived_gen.0): joinpoint Test.7 Test.1: let Test.21 : I64 = 1i64; let Test.9 : I64 = CallByName Num.19 Test.1 Test.21; @@ -33,4 +33,4 @@ procedure Test.0 (#Derived_gen.2): ret Test.8; in - jump Test.7 #Derived_gen.2; + jump Test.7 #Derived_gen.0; diff --git a/crates/compiler/test_mono/generated/inspect_derived_tag_one_field_string.txt b/crates/compiler/test_mono/generated/inspect_derived_tag_one_field_string.txt index 6fec6d18f5c..13c9fae8db0 100644 --- a/crates/compiler/test_mono/generated/inspect_derived_tag_one_field_string.txt +++ b/crates/compiler/test_mono/generated/inspect_derived_tag_one_field_string.txt @@ -162,7 +162,7 @@ procedure List.1 (List.118): let List.677 : Int1 = CallByName Bool.11 List.678 List.679; ret List.677; -procedure List.100 (#Derived_gen.19, #Derived_gen.20, #Derived_gen.21, #Derived_gen.22, #Derived_gen.23): +procedure List.100 (#Derived_gen.23, #Derived_gen.24, #Derived_gen.25, #Derived_gen.26, #Derived_gen.27): joinpoint List.668 List.174 List.175 List.176 List.177 List.178: let List.670 : Int1 = CallByName Num.22 List.177 List.178; if List.670 then @@ -177,8 +177,8 @@ procedure List.100 (#Derived_gen.19, #Derived_gen.20, #Derived_gen.21, #Derived_ dec List.174; ret List.175; in - inc #Derived_gen.19; - jump List.668 #Derived_gen.19 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23; + inc #Derived_gen.23; + jump List.668 #Derived_gen.23 #Derived_gen.24 #Derived_gen.25 #Derived_gen.26 #Derived_gen.27; procedure List.18 (List.171, List.172, List.173): let List.666 : U64 = 0i64; @@ -290,7 +290,7 @@ procedure Str.45 (Str.91, Str.92, Str.93): dec Str.341; ret Str.91; -procedure Str.56 (#Derived_gen.24, #Derived_gen.25, #Derived_gen.26, #Derived_gen.27): +procedure Str.56 (#Derived_gen.19, #Derived_gen.20, #Derived_gen.21, #Derived_gen.22): joinpoint Str.250 Str.96 Str.97 Str.98 Str.99: inc Str.97; let Str.251 : [C {}, C {Str, Str}] = CallByName Str.38 Str.97 Str.98; @@ -314,9 +314,9 @@ procedure Str.56 (#Derived_gen.24, #Derived_gen.25, #Derived_gen.26, #Derived_ge dec Str.97; ret Str.255; in - inc #Derived_gen.27; - inc #Derived_gen.26; - jump Str.250 #Derived_gen.24 #Derived_gen.25 #Derived_gen.26 #Derived_gen.27; + inc #Derived_gen.21; + inc #Derived_gen.22; + jump Str.250 #Derived_gen.19 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22; procedure Str.57 (Str.121, Str.122): let Str.123 : U64 = CallByName Str.36 Str.121; diff --git a/crates/compiler/test_mono/generated/issue_4772_weakened_monomorphic_destructure.txt b/crates/compiler/test_mono/generated/issue_4772_weakened_monomorphic_destructure.txt index 12dc47df8fa..68b16ce5806 100644 --- a/crates/compiler/test_mono/generated/issue_4772_weakened_monomorphic_destructure.txt +++ b/crates/compiler/test_mono/generated/issue_4772_weakened_monomorphic_destructure.txt @@ -91,8 +91,8 @@ procedure Test.19 (): let Test.120 : [C Str, C {List U8, I64}] = TagId(0) Test.122; ret Test.120; else - dec Test.93; dec Test.92; + dec Test.93; let Test.128 : Str = "not a number"; let Test.126 : [C Str, C {List U8, I64}] = TagId(0) Test.128; ret Test.126; diff --git a/crates/compiler/test_mono/generated/linked_list_filter.txt b/crates/compiler/test_mono/generated/linked_list_filter.txt index 8d48bbf5b7d..ef269d33487 100644 --- a/crates/compiler/test_mono/generated/linked_list_filter.txt +++ b/crates/compiler/test_mono/generated/linked_list_filter.txt @@ -8,19 +8,19 @@ procedure Test.2 (Test.4, Test.5): else let Test.7 : I64 = UnionAtIndex (Id 0) (Index 0) Test.4; let Test.8 : [, C I64 *self] = UnionAtIndex (Id 0) (Index 1) Test.4; - joinpoint #Derived_gen.2: + joinpoint #Derived_gen.4: dec Test.8; let Test.22 : Str = "a Lambda Set is empty. Most likely there is a type error in your program."; Crash Test.22 in - let #Derived_gen.3 : Int1 = lowlevel RefCountIsUnique Test.4; - if #Derived_gen.3 then + let #Derived_gen.5 : Int1 = lowlevel RefCountIsUnique Test.4; + if #Derived_gen.5 then free Test.4; - jump #Derived_gen.2; + jump #Derived_gen.4; else inc Test.8; decref Test.4; - jump #Derived_gen.2; + jump #Derived_gen.4; procedure Test.0 (): let Test.27 : I64 = 1i64; @@ -28,15 +28,15 @@ procedure Test.0 (): let Test.30 : [, C I64 *self] = TagId(1) ; let Test.28 : [, C I64 *self] = TagId(0) Test.29 Test.30; let Test.14 : [, C I64 *self] = TagId(0) Test.27 Test.28; - joinpoint #Derived_gen.4: + joinpoint #Derived_gen.2: let Test.26 : Str = "ValueNotExposed { module_name: ModuleName(IdentStr { string: \"Num\" }), ident: Ident(IdentStr { string: \"isEven\" }), region: @416-426, exposed_values: ['max_f32', 'min_f32', 'abs', 'neg', 'add', 'sub', 'mul', 'is_lt', 'is_lte', 'is_gt', 'is_gte', 'to_frac', 'sin', 'cos', 'tan', 'is_zero', 'is_even', 'is_odd', 'is_positive', 'is_negative', 'rem', 'rem_checked', 'div', 'div_checked', 'div_trunc', 'div_trunc_checked', 'sqrt', 'sqrt_checked', 'log', 'log_checked', 'round', 'compare', 'pow', 'ceiling', 'pow_int', 'floor', 'add_wrap', 'add_checked', 'add_saturated', 'atan', 'acos', 'asin', 'bitwise_and', 'bitwise_xor', 'bitwise_or', 'shift_left_by', 'shift_right_by', 'shift_right_zf_by', 'sub_wrap', 'sub_checked', 'sub_saturated', 'mul_wrap', 'mul_checked', 'mul_saturated', 'e', 'pi', 'tau', 'is_multiple_of', 'count_one_bits', 'abs_diff', 'is_nan', 'is_infinite', 'is_finite', 'count_leading_zero_bits', 'count_trailing_zero_bits', 'to_str', 'min_i8', 'max_i8', 'min_u8', 'max_u8', 'min_i16', 'max_i16', 'min_u16', 'max_u16', 'min_i32', 'max_i32', 'min_u32', 'max_u32', 'min_i64', 'max_i64', 'min_u64', 'max_u64', 'min_i128', 'max_i128', 'min_u128', 'max_u128', 'to_i8', 'to_i8_checked', 'to_i16', 'to_i16_checked', 'to_i32', 'to_i32_checked', 'to_i64', 'to_i64_checked', 'to_i128', 'to_i128_checked', 'to_u8', 'to_u8_checked', 'to_u16', 'to_u16_checked', 'to_u32', 'to_u32_checked', 'to_u64', 'to_u64_checked', 'to_u128', 'to_u128_checked', 'div_ceil', 'div_ceil_checked', 'to_f32', 'to_f32_checked', 'to_f64', 'to_f64_checked', 'max_f64', 'min_f64', 'add_checked_lowlevel', 'sub_checked_lowlevel', 'mul_checked_lowlevel', 'min', 'max', 'bitwise_not', 'int_cast', 'is_approx_eq', 'bytes_to_u16_owlevel', 'bytes_to_u32_lowlevel', 'bytes_to_u64_lowlevel', 'bytes_to_u128_lowlevel', 'div_trunc_unchecked', 'rem_unchecked', 'without_decimal_point', 'with_decimal_point', 'f32_to_parts', 'f64_to_parts', 'f32_from_parts', 'f64_from_parts', 'nan_f32', 'nan_f64', 'infinity_f32', 'infinity_f64', 'from_bool'] }"; Crash Test.26 in - let #Derived_gen.5 : Int1 = lowlevel RefCountIsUnique Test.14; - if #Derived_gen.5 then + let #Derived_gen.3 : Int1 = lowlevel RefCountIsUnique Test.14; + if #Derived_gen.3 then dec Test.28; free Test.14; - jump #Derived_gen.4; + jump #Derived_gen.2; else decref Test.14; - jump #Derived_gen.4; + jump #Derived_gen.2; diff --git a/crates/compiler/test_mono/generated/list_map_take_capturing_or_noncapturing.txt b/crates/compiler/test_mono/generated/list_map_take_capturing_or_noncapturing.txt index 26525f60c02..bc7ae93e6bd 100644 --- a/crates/compiler/test_mono/generated/list_map_take_capturing_or_noncapturing.txt +++ b/crates/compiler/test_mono/generated/list_map_take_capturing_or_noncapturing.txt @@ -108,8 +108,8 @@ procedure Test.0 (): else let Test.22 : Str = "B"; let Test.23 : Int1 = lowlevel Eq Test.22 Test.12; - dec Test.12; dec Test.22; + dec Test.12; if Test.23 then let Test.17 : [C U8, C U8, C ] = TagId(1) Test.2; jump Test.13 Test.17; diff --git a/crates/compiler/test_mono/generated/pattern_as_toplevel.txt b/crates/compiler/test_mono/generated/pattern_as_toplevel.txt index a124668cf09..4a70501f514 100644 --- a/crates/compiler/test_mono/generated/pattern_as_toplevel.txt +++ b/crates/compiler/test_mono/generated/pattern_as_toplevel.txt @@ -20,9 +20,9 @@ procedure Test.0 (): if Test.13 then let Test.6 : {I64, Str} = CallByName Test.1; let Test.5 : Int1 = CallByName Bool.11 Test.6 Test.4; + dec Test.6; let #Derived_gen.0 : Str = StructAtIndex 1 Test.4; dec #Derived_gen.0; - dec Test.6; ret Test.5; else let #Derived_gen.1 : Str = StructAtIndex 1 Test.4; diff --git a/crates/compiler/test_mono/generated/recursive_lambda_set_resolved_only_upon_specialization.txt b/crates/compiler/test_mono/generated/recursive_lambda_set_resolved_only_upon_specialization.txt index b1bd5081217..2f88d6d8076 100644 --- a/crates/compiler/test_mono/generated/recursive_lambda_set_resolved_only_upon_specialization.txt +++ b/crates/compiler/test_mono/generated/recursive_lambda_set_resolved_only_upon_specialization.txt @@ -10,7 +10,7 @@ procedure Num.21 (#Attr.2, #Attr.3): let Num.283 : U8 = lowlevel NumMul #Attr.2 #Attr.3; ret Num.283; -procedure Test.1 (#Derived_gen.2, #Derived_gen.3): +procedure Test.1 (#Derived_gen.0, #Derived_gen.1): joinpoint Test.11 Test.2 Test.3: let Test.26 : U8 = 0i64; let Test.22 : Int1 = CallByName Bool.11 Test.2 Test.26; @@ -33,9 +33,9 @@ procedure Test.1 (#Derived_gen.2, #Derived_gen.3): let Test.14 : [, C *self U8] = TagId(0) Test.3 Test.2; jump Test.11 Test.13 Test.14; in - jump Test.11 #Derived_gen.2 #Derived_gen.3; + jump Test.11 #Derived_gen.0 #Derived_gen.1; -procedure Test.4 (#Derived_gen.0, #Derived_gen.1): +procedure Test.4 (#Derived_gen.2, #Derived_gen.3): joinpoint Test.15 Test.5 #Attr.12: let Test.20 : U8 = UnionAtIndex (Id 0) (Index 1) #Attr.12; let Test.19 : [, C *self U8] = UnionAtIndex (Id 0) (Index 0) #Attr.12; @@ -61,7 +61,7 @@ procedure Test.4 (#Derived_gen.0, #Derived_gen.1): decref #Attr.12; jump #Derived_gen.4; in - jump Test.15 #Derived_gen.0 #Derived_gen.1; + jump Test.15 #Derived_gen.2 #Derived_gen.3; procedure Test.6 (Test.7): ret Test.7; diff --git a/crates/compiler/test_mono/generated/recursively_build_effect.txt b/crates/compiler/test_mono/generated/recursively_build_effect.txt index 391c13c39b5..f43506bad37 100644 --- a/crates/compiler/test_mono/generated/recursively_build_effect.txt +++ b/crates/compiler/test_mono/generated/recursively_build_effect.txt @@ -8,8 +8,8 @@ procedure Str.3 (#Attr.2, #Attr.3): procedure Test.11 (Test.29, #Attr.12): let Test.32 : {} = UnionAtIndex (Id 0) (Index 0) #Attr.12; - let #Derived_gen.11 : Int1 = lowlevel RefCountIsUnique #Attr.12; - if #Derived_gen.11 then + let #Derived_gen.9 : Int1 = lowlevel RefCountIsUnique #Attr.12; + if #Derived_gen.9 then free #Attr.12; ret Test.32; else @@ -19,11 +19,11 @@ procedure Test.11 (Test.29, #Attr.12): procedure Test.11 (Test.29, Test.10): ret Test.10; -procedure Test.14 (#Derived_gen.0, #Derived_gen.1): +procedure Test.14 (#Derived_gen.7, #Derived_gen.8): joinpoint Test.38 Test.37 #Attr.12: let Test.46 : {} = UnionAtIndex (Id 1) (Index 1) #Attr.12; let Test.45 : I64 = UnionAtIndex (Id 1) (Index 0) #Attr.12; - joinpoint #Derived_gen.9: + joinpoint #Derived_gen.10: let Test.44 : {} = Struct {}; let Test.43 : {} = CallByName Test.11 Test.44 Test.46; let Test.39 : [C {}, C I64 {}] = CallByName Test.9 Test.43 Test.45; @@ -38,15 +38,15 @@ procedure Test.14 (#Derived_gen.0, #Derived_gen.1): jump Test.38 Test.41 Test.39; in - let #Derived_gen.10 : Int1 = lowlevel RefCountIsUnique #Attr.12; - if #Derived_gen.10 then + let #Derived_gen.11 : Int1 = lowlevel RefCountIsUnique #Attr.12; + if #Derived_gen.11 then free #Attr.12; - jump #Derived_gen.9; + jump #Derived_gen.10; else decref #Attr.12; - jump #Derived_gen.9; + jump #Derived_gen.10; in - jump Test.38 #Derived_gen.0 #Derived_gen.1; + jump Test.38 #Derived_gen.7 #Derived_gen.8; procedure Test.2 (): let Test.6 : Str = "Hello"; diff --git a/crates/compiler/test_mono/generated/specialize_after_match.txt b/crates/compiler/test_mono/generated/specialize_after_match.txt index 2feb395e376..474578aa329 100644 --- a/crates/compiler/test_mono/generated/specialize_after_match.txt +++ b/crates/compiler/test_mono/generated/specialize_after_match.txt @@ -23,7 +23,7 @@ procedure Test.2 (Test.9, Test.10): let Test.29 : U64 = CallByName Test.3 Test.9; ret Test.29; else - joinpoint #Derived_gen.1: + joinpoint #Derived_gen.4: let Test.13 : Str = UnionAtIndex (Id 0) (Index 0) Test.10; let Test.14 : [, C Str *self] = UnionAtIndex (Id 0) (Index 1) Test.10; let Test.33 : U64 = CallByName Test.3 Test.12; @@ -36,15 +36,15 @@ procedure Test.2 (Test.9, Test.10): else ret Test.16; in - let #Derived_gen.2 : Int1 = lowlevel RefCountIsUnique Test.9; - if #Derived_gen.2 then + let #Derived_gen.5 : Int1 = lowlevel RefCountIsUnique Test.9; + if #Derived_gen.5 then dec Test.11; free Test.9; - jump #Derived_gen.1; + jump #Derived_gen.4; else inc Test.12; decref Test.9; - jump #Derived_gen.1; + jump #Derived_gen.4; procedure Test.3 (Test.17): let Test.26 : U8 = 1i64; @@ -55,22 +55,22 @@ procedure Test.3 (Test.17): ret Test.22; else let Test.18 : [, C Str *self] = UnionAtIndex (Id 0) (Index 1) Test.17; - joinpoint #Derived_gen.3: + joinpoint #Derived_gen.1: let Test.24 : U64 = 1i64; let Test.25 : U64 = CallByName Test.3 Test.18; let Test.23 : U64 = CallByName Num.19 Test.24 Test.25; ret Test.23; in - let #Derived_gen.5 : Int1 = lowlevel RefCountIsUnique Test.17; - if #Derived_gen.5 then - let #Derived_gen.4 : Str = UnionAtIndex (Id 0) (Index 0) Test.17; - dec #Derived_gen.4; + let #Derived_gen.3 : Int1 = lowlevel RefCountIsUnique Test.17; + if #Derived_gen.3 then + let #Derived_gen.2 : Str = UnionAtIndex (Id 0) (Index 0) Test.17; + dec #Derived_gen.2; free Test.17; - jump #Derived_gen.3; + jump #Derived_gen.1; else inc Test.18; decref Test.17; - jump #Derived_gen.3; + jump #Derived_gen.1; procedure Test.0 (): let Test.5 : [, C Str *self] = TagId(1) ; diff --git a/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_does_not_duplicate_identical_concrete_types.txt b/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_does_not_duplicate_identical_concrete_types.txt index 55e453bdf1f..b9777140639 100644 --- a/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_does_not_duplicate_identical_concrete_types.txt +++ b/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_does_not_duplicate_identical_concrete_types.txt @@ -29,7 +29,7 @@ procedure Encode.26 (Encode.107, Encode.108): let Encode.110 : List U8 = CallByName Encode.24 Encode.111 Encode.112 Encode.108; ret Encode.110; -procedure List.100 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4): +procedure List.100 (#Derived_gen.6, #Derived_gen.7, #Derived_gen.8, #Derived_gen.9, #Derived_gen.10): joinpoint List.668 List.174 List.175 List.176 List.177 List.178: let List.670 : Int1 = CallByName Num.22 List.177 List.178; if List.670 then @@ -43,8 +43,8 @@ procedure List.100 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen dec List.174; ret List.175; in - inc #Derived_gen.0; - jump List.668 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; + inc #Derived_gen.6; + jump List.668 #Derived_gen.6 #Derived_gen.7 #Derived_gen.8 #Derived_gen.9 #Derived_gen.10; procedure List.13 (#Attr.2, #Attr.3): let List.691 : List Str = lowlevel ListPrepend #Attr.2 #Attr.3; diff --git a/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification_of_unifiable.txt b/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification_of_unifiable.txt index e8ea2275839..974cea5e21c 100644 --- a/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification_of_unifiable.txt +++ b/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification_of_unifiable.txt @@ -87,7 +87,7 @@ procedure Encode.26 (Encode.107, Encode.108): let Encode.110 : List U8 = CallByName Encode.24 Encode.111 Encode.112 Encode.108; ret Encode.110; -procedure List.100 (#Derived_gen.44, #Derived_gen.45, #Derived_gen.46, #Derived_gen.47, #Derived_gen.48): +procedure List.100 (#Derived_gen.29, #Derived_gen.30, #Derived_gen.31, #Derived_gen.32, #Derived_gen.33): joinpoint List.695 List.174 List.175 List.176 List.177 List.178: let List.697 : Int1 = CallByName Num.22 List.177 List.178; if List.697 then @@ -101,8 +101,8 @@ procedure List.100 (#Derived_gen.44, #Derived_gen.45, #Derived_gen.46, #Derived_ dec List.174; ret List.175; in - inc #Derived_gen.44; - jump List.695 #Derived_gen.44 #Derived_gen.45 #Derived_gen.46 #Derived_gen.47 #Derived_gen.48; + inc #Derived_gen.29; + jump List.695 #Derived_gen.29 #Derived_gen.30 #Derived_gen.31 #Derived_gen.32 #Derived_gen.33; procedure List.100 (#Derived_gen.49, #Derived_gen.50, #Derived_gen.51, #Derived_gen.52, #Derived_gen.53): joinpoint List.668 List.174 List.175 List.176 List.177 List.178: diff --git a/crates/compiler/test_syntax/src/test_helpers.rs b/crates/compiler/test_syntax/src/test_helpers.rs index 098331e9da5..4e1d5d55a2b 100644 --- a/crates/compiler/test_syntax/src/test_helpers.rs +++ b/crates/compiler/test_syntax/src/test_helpers.rs @@ -208,7 +208,6 @@ impl<'a> Output<'a> { &dep_idents, &qualified_module_ids, None, - roc_can::env::FxMode::PurityInference, ); // Desugar operators (convert them to Apply calls, taking into account diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/all_the_bangs.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/all_the_bangs.expr.result-ast new file mode 100644 index 00000000000..41f3bcca665 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/fail/all_the_bangs.expr.result-ast @@ -0,0 +1 @@ +Expr(IndentStart(@8), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/all_the_bangs.expr.roc b/crates/compiler/test_syntax/tests/snapshots/fail/all_the_bangs.expr.roc new file mode 100644 index 00000000000..5fec70a574b --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/fail/all_the_bangs.expr.roc @@ -0,0 +1,3 @@ +p +! +.p!! diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/exponential_else_branch_parsing_repro.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/exponential_else_branch_parsing_repro.expr.result-ast index b0d80ebdf27..de48af82eab 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/exponential_else_branch_parsing_repro.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/exponential_else_branch_parsing_repro.expr.result-ast @@ -1 +1 @@ -Expr(If(Condition(If(Condition(If(Condition(If(Condition(If(Condition(If(Condition(If(Condition(If(Condition(If(Condition(If(Condition(If(Condition(If(Condition(If(Condition(If(Condition(If(Condition(If(Condition(If(ElseBranch(Start(@341), @341), @288), @277), @275), @261), @259), @243), @241), @229), @227), @213), @211), @194), @192), @186), @184), @165), @163), @151), @149), @135), @133), @116), @114), @108), @106), @92), @90), @80), @78), @64), @62), @34), @32), @0) \ No newline at end of file +Expr(BadExprEnd(@21), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/all_the_bangs.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/all_the_bangs.expr.formatted.roc deleted file mode 100644 index c61275fdf6a..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/all_the_bangs.expr.formatted.roc +++ /dev/null @@ -1,2 +0,0 @@ -p -! .p!! \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/all_the_bangs.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/all_the_bangs.expr.result-ast deleted file mode 100644 index 1e7e3d9775b..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/all_the_bangs.expr.result-ast +++ /dev/null @@ -1,47 +0,0 @@ -@0-8 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-1, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - Stmt( - @0-1 Var { - module_name: "", - ident: "p", - }, - ), - ], - }, - @2-8 SpaceBefore( - UnaryOp( - @4-7 SpaceBefore( - TrySuffix { - target: Task, - expr: AccessorFunction( - RecordField( - "p!", - ), - ), - }, - [ - Newline, - ], - ), - @2-3 Not, - ), - [ - Newline, - ], - ), -) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/all_the_bangs.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/all_the_bangs.expr.roc deleted file mode 100644 index a73d30e9bf4..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/all_the_bangs.expr.roc +++ /dev/null @@ -1,3 +0,0 @@ -p -! -.p!! \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/bangs_and_tuple_accessors.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/bangs_and_tuple_accessors.expr.formatted.roc index 9f9ee910d04..68965274922 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/bangs_and_tuple_accessors.expr.formatted.roc +++ b/crates/compiler/test_syntax/tests/snapshots/pass/bangs_and_tuple_accessors.expr.formatted.roc @@ -1,2 +1,3 @@ J -! .1!.0 \ No newline at end of file +! .1 + ! .0 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/bangs_and_tuple_accessors.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/bangs_and_tuple_accessors.expr.result-ast index e66ce1ad93d..d690fb30d85 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/bangs_and_tuple_accessors.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/bangs_and_tuple_accessors.expr.result-ast @@ -24,24 +24,31 @@ ], }, @2-9 SpaceBefore( - UnaryOp( - @4-6 SpaceBefore( - TupleAccess( - TrySuffix { - target: Task, - expr: AccessorFunction( - TupleIndex( - "1", - ), + Apply( + @2-6 UnaryOp( + @4-6 SpaceBefore( + AccessorFunction( + TupleIndex( + "1", ), - }, - "0", + ), + [ + Newline, + ], ), - [ - Newline, - ], + @2-3 Not, ), - @2-3 Not, + [ + @6-9 UnaryOp( + @7-9 AccessorFunction( + TupleIndex( + "0", + ), + ), + @6-7 Not, + ), + ], + Space, ), [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/closure_in_apply_in_binop.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/closure_in_apply_in_binop.expr.result-ast index bb7e2155cd5..1e9717807bf 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/closure_in_apply_in_binop.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/closure_in_apply_in_binop.expr.result-ast @@ -15,13 +15,12 @@ }, ], @6-9 Apply( - @6-7 TrySuffix { - target: Result, - expr: Var { + @6-7 TrySuffix( + Var { module_name: "", ident: "w", }, - }, + ), [ @8-9 Var { module_name: "", diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.result-ast index e215ef27e28..ae381475bdd 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.result-ast @@ -62,13 +62,12 @@ Defs { ), Stmt( @97-111 Apply( - @97-108 TrySuffix { - target: Result, - expr: Var { + @97-108 TrySuffix( + Var { module_name: "Stdout", ident: "line", }, - }, + ), [ @110-111 Var { module_name: "", @@ -81,13 +80,12 @@ Defs { ], }, @141-150 SpaceBefore( - TrySuffix { - target: Result, - expr: Var { + TrySuffix( + Var { module_name: "", ident: "printBar", }, - }, + ), [ Newline, Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/function_effect_types.header.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/function_effect_types.header.formatted.roc index 5cfd7e58c44..b80f1b040b3 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/function_effect_types.header.formatted.roc +++ b/crates/compiler/test_syntax/tests/snapshots/pass/function_effect_types.header.formatted.roc @@ -1,6 +1,6 @@ platform "cli" - requires {} { main : Task {} [] } # TODO FIXME + requires {} { main! : {} => Result {} [] } # TODO FIXME exposes [] packages {} - imports [Task.{ Task }] + imports [Foo.{ Foo }] provides [main_for_host] diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/function_effect_types.header.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/function_effect_types.header.result-ast index 17232e5d13f..f1e476978b1 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/function_effect_types.header.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/function_effect_types.header.result-ast @@ -17,22 +17,31 @@ SpacesBefore { item: PlatformRequires { rigids: [], signatures: [ - @32-49 TypedIdent { - ident: @32-36 "main", + @32-58 TypedIdent { + ident: @32-37 "main!", spaces_before_colon: [], - ann: @39-49 Apply( - "", - "Task", + ann: @40-58 Function( [ - @44-46 Record { + @40-42 Record { fields: [], ext: None, }, - @47-49 TagUnion { - ext: None, - tags: [], - }, ], + Effectful, + @46-58 Apply( + "", + "Result", + [ + @53-55 Record { + fields: [], + ext: None, + }, + @56-58 TagUnion { + ext: None, + tags: [], + }, + ], + ), ), }, ], @@ -69,13 +78,13 @@ SpacesBefore { after: [], }, item: [ - @110-123 Module( + @119-130 Module( ModuleName( - "Task", + "Foo", ), [ - @117-121 ExposedName( - "Task", + @125-128 ExposedName( + "Foo", ), ], ), @@ -90,7 +99,7 @@ SpacesBefore { after: [], }, item: [ - @141-154 ExposedName( + @148-161 ExposedName( "main_for_host", ), ], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/function_effect_types.header.roc b/crates/compiler/test_syntax/tests/snapshots/pass/function_effect_types.header.roc index 0287d20de44..4b365822fee 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/function_effect_types.header.roc +++ b/crates/compiler/test_syntax/tests/snapshots/pass/function_effect_types.header.roc @@ -1,6 +1,6 @@ platform "cli" - requires {}{ main : Task {} [] } # TODO FIXME + requires {}{ main! : {} => Result {} [] } # TODO FIXME exposes [] packages {} - imports [ Task.{ Task } ] + imports [ Foo.{ Foo } ] provides [ main_for_host ] diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/pizza_question.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/pizza_question.moduledefs.result-ast index 62f899836fa..e1ba4cff234 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/pizza_question.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/pizza_question.moduledefs.result-ast @@ -26,13 +26,12 @@ Defs { ( @11-24 SpaceAfter( Apply( - @11-20 TrySuffix { - target: Result, - expr: Var { + @11-20 TrySuffix( + Var { module_name: "", ident: "parseArgs", }, - }, + ), [ @22-24 Record( [], @@ -69,13 +68,12 @@ Defs { ( @56-77 SpaceAfter( Apply( - @56-67 TrySuffix { - target: Result, - expr: Var { + @56-67 TrySuffix( + Var { module_name: "List", ident: "mapTry", }, - }, + ), [ @69-77 Var { module_name: "Str", diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_updater_closure_weirdness.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/record_updater_closure_weirdness.expr.result-ast index 6bcf31ef33c..073a2869bc8 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/record_updater_closure_weirdness.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_updater_closure_weirdness.expr.result-ast @@ -4,12 +4,11 @@ ( @0-10 SpaceAfter( Apply( - @0-3 TrySuffix { - target: Result, - expr: RecordUpdater( + @0-3 TrySuffix( + RecordUpdater( "rm", ), - }, + ), [ @4-10 Closure( [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question.expr.result-ast index ed07176da73..7f8b657826f 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question.expr.result-ast @@ -1,13 +1,10 @@ -@0-14 TrySuffix { - target: Result, - expr: TrySuffix { - target: Result, - expr: TrySuffix { - target: Result, - expr: Var { +@0-14 TrySuffix( + TrySuffix( + TrySuffix( + Var { module_name: "Stdout", ident: "line", }, - }, - }, -} + ), + ), +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.result-ast index fa364203963..27df956c71d 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.result-ast @@ -46,13 +46,12 @@ Defs { value_defs: [ Stmt( @12-21 Apply( - @12-13 TrySuffix { - target: Result, - expr: Var { + @12-13 TrySuffix( + Var { module_name: "", ident: "a", }, - }, + ), [ @16-21 Str( PlainLine( @@ -68,13 +67,12 @@ Defs { ident: "x", }, @29-39 Apply( - @29-32 TrySuffix { - target: Result, - expr: Var { + @29-32 TrySuffix( + Var { module_name: "B", ident: "b", }, - }, + ), [ @34-39 Str( PlainLine( @@ -89,13 +87,12 @@ Defs { }, @45-49 SpaceBefore( Apply( - @45-46 TrySuffix { - target: Result, - expr: Var { + @45-46 TrySuffix( + Var { module_name: "", ident: "c", }, - }, + ), [ @48-49 Var { module_name: "", diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_nested.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_nested.expr.result-ast index b49b68f9fa6..dc0fcc031cf 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_nested.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_nested.expr.result-ast @@ -1,21 +1,19 @@ @0-33 Apply( - @0-3 TrySuffix { - target: Result, - expr: Var { + @0-3 TrySuffix( + Var { module_name: "", ident: "foo", }, - }, + ), [ @9-17 ParensAround( Apply( - @9-12 TrySuffix { - target: Result, - expr: Var { + @9-12 TrySuffix( + Var { module_name: "", ident: "bar", }, - }, + ), [ @14-17 Var { module_name: "", diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.result-ast index 95de78c1a00..79e7be3217f 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.result-ast @@ -123,13 +123,12 @@ Full( @126-128 Pizza, ), ], - @129-132 TrySuffix { - target: Result, - expr: Var { + @129-132 TrySuffix( + Var { module_name: "A", ident: "x", }, - }, + ), ), ), Stmt( @@ -145,13 +144,12 @@ Full( ), ], @171-205 Apply( - @171-174 TrySuffix { - target: Result, - expr: Var { + @171-174 TrySuffix( + Var { module_name: "B", ident: "y", }, - }, + ), [ @185-205 SpaceBefore( Record( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.result-ast index ffc0bce3693..d4d02147b00 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.result-ast @@ -108,13 +108,12 @@ Full( ), ], @167-204 Apply( - @167-180 TrySuffix { - target: Result, - expr: Var { + @167-180 TrySuffix( + Var { module_name: "Result", ident: "mapErr", }, - }, + ), [ @182-204 Tag( "UnableToCheckJQVersion", diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/type_decl_with_underscore.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/type_decl_with_underscore.expr.formatted.roc new file mode 100644 index 00000000000..645e0429ef2 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/type_decl_with_underscore.expr.formatted.roc @@ -0,0 +1,2 @@ +doStuff : UserId -> Dict Str _ +42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/type_decl_with_underscore.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/type_decl_with_underscore.expr.result-ast index 8b2835b4386..e0f46a2b4cb 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/type_decl_with_underscore.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/type_decl_with_underscore.expr.result-ast @@ -1,55 +1,60 @@ -@0-33 Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @0-30, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - Annotation( - @0-7 Identifier { - ident: "doStuff", - }, - @10-30 Function( - [ - @10-16 Apply( - "", - "UserId", - [], - ), - ], - Pure, - @20-30 Apply( - "", - "Task", +@0-33 SpaceAfter( + Defs( + Defs { + tags: [ + EitherIndex(2147483648), + ], + regions: [ + @0-30, + ], + space_before: [ + Slice { start: 0, length: 0 }, + ], + space_after: [ + Slice { start: 0, length: 0 }, + ], + spaces: [], + type_defs: [], + value_defs: [ + Annotation( + @0-7 Identifier { + ident: "doStuff", + }, + @10-30 Function( [ - @25-28 Apply( + @10-16 Apply( "", - "Str", + "UserId", [], ), - @29-30 Inferred, ], + Pure, + @20-30 Apply( + "", + "Dict", + [ + @25-28 Apply( + "", + "Str", + [], + ), + @29-30 Inferred, + ], + ), ), ), + ], + }, + @31-33 SpaceBefore( + Num( + "42", ), - ], - }, - @31-33 SpaceBefore( - Num( - "42", + [ + Newline, + ], ), - [ - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/type_decl_with_underscore.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/type_decl_with_underscore.expr.roc index 49d06baf926..cb96547127f 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/type_decl_with_underscore.expr.roc +++ b/crates/compiler/test_syntax/tests/snapshots/pass/type_decl_with_underscore.expr.roc @@ -1,2 +1,2 @@ -doStuff : UserId -> Task Str _ -42 \ No newline at end of file +doStuff : UserId -> Dict Str _ +42 diff --git a/crates/compiler/test_syntax/tests/test_snapshots.rs b/crates/compiler/test_syntax/tests/test_snapshots.rs index 94f3aab8ad2..4d9c44565df 100644 --- a/crates/compiler/test_syntax/tests/test_snapshots.rs +++ b/crates/compiler/test_syntax/tests/test_snapshots.rs @@ -189,6 +189,7 @@ mod test_snapshots { fail/ability_demands_not_indented_with_first.expr, fail/ability_first_demand_not_indented_enough.expr, fail/ability_non_signature_expression.expr, + fail/all_the_bangs.expr, fail/alias_or_opaque_fail.expr, fail/bound_variable.expr, fail/comment_with_tab.expr, @@ -295,7 +296,6 @@ mod test_snapshots { pass/alias_comment_after_head.expr, pass/alias_parens_comment.expr, pass/alias_parens_comment_indent.expr, - pass/all_the_bangs.expr, pass/ann_apply_record_with_newlines.expr, pass/ann_closed_union.expr, pass/ann_effectful_fn.expr, @@ -780,15 +780,12 @@ mod test_snapshots { /// Does the given test name expect the canonicalization process to panic? fn expect_canonicalize_panics(test_name: &str) -> bool { + #[allow(clippy::match_single_binding)] match test_name { // This is the current list as of writing. // We should be driving these down to zero over time. // Adding this protection in now to avoid accidentally adding more. - "all_the_bangs" | "bangs_and_tuple_accessors" => { - // both of these result in: - // "a Expr::TrySuffix expression was not completely removed in desugar_value_def_suffixed" - true - } + // NOTHING FOR NOW! // When adding new snapshot tests, strongly prefer fixing any canonicalization panics // they may run into rather than adding them to this list. diff --git a/crates/glue/src/types.rs b/crates/glue/src/types.rs index 1ad014ad18d..3f6c95f4cd2 100644 --- a/crates/glue/src/types.rs +++ b/crates/glue/src/types.rs @@ -1538,30 +1538,6 @@ fn add_type_help<'a>( type_id } - LayoutRepr::LambdaSet(_lambda_set) if *name == Symbol::TASK_TASK => { - let type_vars = env.subs.get_subs_slice(alias_vars.type_variables()); - debug_assert_eq!(type_vars.len(), 2); - - let ok_var = type_vars[0]; - let ok_layout = env.layout_cache.from_var(env.arena, ok_var, subs).unwrap(); - let ok_id = add_type_help(env, ok_layout, ok_var, None, types); - - let err_var = type_vars[1]; - let err_layout = - env.layout_cache.from_var(env.arena, err_var, subs).unwrap(); - let err_id = add_type_help(env, err_layout, err_var, None, types); - - let type_id = types.add_anonymous( - &env.layout_cache.interner, - RocType::Unsized, - layout, - ); - - types.depends(type_id, ok_id); - types.depends(type_id, err_id); - - type_id - } _ => { unreachable!() } diff --git a/crates/language_server/src/analysis.rs b/crates/language_server/src/analysis.rs index 1d3631de993..9b1a1283fd0 100644 --- a/crates/language_server/src/analysis.rs +++ b/crates/language_server/src/analysis.rs @@ -192,7 +192,7 @@ pub(crate) fn global_analysis(doc_info: DocInfo) -> Vec { /// Take the exposed imports from each module, lookup the symbol within that module's list of /// exposed symbols and then get the type info for that import. -/// example: `import {Task.{await}}`. `await` is an exposed_import, so we need to lookup its type info. +/// example: `import Foo exposing [bar]`. `bar` is an exposed_import, so we need to lookup its type info. fn resolve_exposed_imports( exposed_imports: MutMap>, exposes: &MutMap>, diff --git a/crates/language_server/src/analysis/tokens.rs b/crates/language_server/src/analysis/tokens.rs index 94206693a5e..438baef6d4e 100644 --- a/crates/language_server/src/analysis/tokens.rs +++ b/crates/language_server/src/analysis/tokens.rs @@ -656,7 +656,7 @@ impl IterTokens for Loc> { Expr::AccessorFunction(accessor) => Loc::at(region, accessor).iter_tokens(arena), Expr::RecordUpdater(updater) => Loc::at(region, updater).iter_tokens(arena), Expr::TupleAccess(tup, _field) => Loc::at(region, *tup).iter_tokens(arena), - Expr::TrySuffix { expr: inner, .. } => Loc::at(region, *inner).iter_tokens(arena), + Expr::TrySuffix(inner) => Loc::at(region, *inner).iter_tokens(arena), Expr::List(lst) => lst.iter_tokens(arena), Expr::RecordUpdate { update, fields } => (update.iter_tokens(arena).into_iter()) .chain(fields.iter().flat_map(|f| f.iter_tokens(arena))) @@ -725,9 +725,7 @@ impl IterTokens for Loc> { Expr::EmptyRecordBuilder(e) => e.iter_tokens(arena), Expr::SingleFieldRecordBuilder(e) => e.iter_tokens(arena), Expr::OptionalFieldInRecordBuilder(_name, e) => e.iter_tokens(arena), - Expr::MalformedIdent(_, _) - | Expr::PrecedenceConflict(_) - | Expr::MalformedSuffixed(_) => { + Expr::MalformedIdent(_, _) | Expr::PrecedenceConflict(_) => { bumpvec![in arena;] } } diff --git a/crates/reporting/src/error/canonicalize.rs b/crates/reporting/src/error/canonicalize.rs index 7b900f22e51..4cd9ae774e8 100644 --- a/crates/reporting/src/error/canonicalize.rs +++ b/crates/reporting/src/error/canonicalize.rs @@ -63,7 +63,6 @@ const ABILITY_IMPLEMENTATION_NOT_IDENTIFIER: &str = "ABILITY IMPLEMENTATION NOT const DUPLICATE_IMPLEMENTATION: &str = "DUPLICATE IMPLEMENTATION"; const UNNECESSARY_IMPLEMENTATIONS: &str = "UNNECESSARY IMPLEMENTATIONS"; const INCOMPLETE_ABILITY_IMPLEMENTATION: &str = "INCOMPLETE ABILITY IMPLEMENTATION"; -const STATEMENT_AFTER_EXPRESSION: &str = "STATEMENT AFTER EXPRESSION"; const MISSING_EXCLAMATION: &str = "MISSING EXCLAMATION"; const UNNECESSARY_EXCLAMATION: &str = "UNNECESSARY EXCLAMATION"; const EMPTY_TUPLE_TYPE: &str = "EMPTY TUPLE TYPE"; @@ -1392,32 +1391,6 @@ pub fn can_problem<'b>( title = "UNNECESSARY RETURN".to_string(); } - Problem::StmtAfterExpr(region) => { - doc = alloc.stack([ - alloc - .reflow(r"I just finished parsing an expression with a series of definitions,"), - alloc.reflow( - r"and this line is indented as if it's intended to be part of that expression:", - ), - alloc.region(lines.convert_region(region), severity), - alloc.concat([alloc.reflow( - "However, I already saw the final expression in that series of definitions.", - )]), - alloc.tip().append( - alloc.reflow( - "An expression like `4`, `\"hello\"`, or `function_call(MyThing)` is like `return 4` in other programming languages. To me, it seems like you did `return 4` followed by more code in the lines after, that code would never be executed!" - ) - ), - alloc.tip().append( - alloc.reflow( - "If you are working with `Task`, this error can happen if you forgot a `!` somewhere." - ) - ) - ]); - - title = STATEMENT_AFTER_EXPRESSION.to_string(); - } - Problem::UnsuffixedEffectfulRecordField(region) => { doc = alloc.stack([ alloc.reflow( @@ -2227,9 +2200,6 @@ fn pretty_runtime_error<'b>( title = SYNTAX_PROBLEM; } - RuntimeError::MalformedSuffixed(_) => { - todo!("error for malformed suffix"); - } RuntimeError::InvalidFloat(sign @ FloatErrorKind::PositiveInfinity, region, _raw_str) | RuntimeError::InvalidFloat(sign @ FloatErrorKind::NegativeInfinity, region, _raw_str) => { let tip = alloc @@ -2671,6 +2641,15 @@ fn pretty_runtime_error<'b>( title = "OPTIONAL FIELD IN RECORD BUILDER"; } + RuntimeError::NonFunctionHostedAnnotation(region) => { + doc = alloc.stack([ + alloc.reflow("This hosted annotation is not for a function:"), + alloc.region(lines.convert_region(region), severity), + alloc.reflow("Only functions can be configured for FFI with the host."), + ]); + + title = "NON-FUNCTION HOSTED ANNOTATION"; + } } (doc, title) diff --git a/crates/reporting/src/error/parse.rs b/crates/reporting/src/error/parse.rs index 4784ed0f64f..13e01addd84 100644 --- a/crates/reporting/src/error/parse.rs +++ b/crates/reporting/src/error/parse.rs @@ -4288,7 +4288,7 @@ fn to_requires_report<'a>( alloc.reflow(" keyword next, like"), ]), alloc - .parser_suggestion("requires { main : Task I64 Str }") + .parser_suggestion("requires { main! : {} => Result I64 Str }") .indent(4), ]); @@ -4315,7 +4315,7 @@ fn to_requires_report<'a>( alloc.reflow(" keyword next, like"), ]), alloc - .parser_suggestion("requires { main : Task I64 Str }") + .parser_suggestion("requires { main! : {} => Result I64 Str }") .indent(4), ]); @@ -4338,13 +4338,13 @@ fn to_requires_report<'a>( alloc.reflow("I am expecting a list of rigids like "), alloc.keyword("{}"), alloc.reflow(" or "), - alloc.keyword("{model=>Model}"), + alloc.keyword("{ Model }"), alloc.reflow(" next. A full "), alloc.keyword("requires"), alloc.reflow(" definition looks like"), ]), alloc - .parser_suggestion("requires {model=>Model, msg=>Msg} {main : Task {} []}") + .parser_suggestion("requires { Model, Msg } { main! : {} => Result {} [] }") .indent(4), ]); @@ -4373,7 +4373,7 @@ fn to_requires_report<'a>( alloc.reflow(" definition looks like"), ]), alloc - .parser_suggestion("requires { Model, Msg } {main : Task {} []}") + .parser_suggestion("requires { Model, Msg } { main! : {} => Result {} [] }") .indent(4), ]); diff --git a/crates/test_compile/src/help_can.rs b/crates/test_compile/src/help_can.rs index 8b42b069ee8..a743eee127d 100644 --- a/crates/test_compile/src/help_can.rs +++ b/crates/test_compile/src/help_can.rs @@ -71,7 +71,6 @@ impl CanExpr { &dep_idents, &qualified_module_ids, None, - roc_can::env::FxMode::PurityInference, ); // Desugar operators (convert them to Apply calls, taking into account diff --git a/examples/glue/glue.roc b/examples/glue/glue.roc deleted file mode 100644 index 912853e350e..00000000000 --- a/examples/glue/glue.roc +++ /dev/null @@ -1,7 +0,0 @@ -app [main] { pf: platform "rust-platform/main.roc" } - -main = - msg = "Roc <3 Rust, also on stderr!\n" - StdoutWrite("Roc <3 Rust!\n", \{} -> - StderrWrite(msg, \{} -> - Done)) diff --git a/examples/glue/rust-platform/main.roc b/examples/glue/rust-platform/main.roc deleted file mode 100644 index 657653f7e2e..00000000000 --- a/examples/glue/rust-platform/main.roc +++ /dev/null @@ -1,17 +0,0 @@ -platform "echo-in-rust" - requires {} { main : _ } - exposes [] - packages {} - imports [] - provides [main_for_host] - -# main_for_host : [StdoutWrite Str (({} -> Op) as Fx0), StderrWrite Str (({} -> Op) as Fx1), Done] as Op -main_for_host : [StdoutWrite Str ({} -> Op), StderrWrite Str ({} -> Op), Done] as Op -main_for_host = main - -# main_for_host : { x: Str, y: {} -> Str } -# main_for_host = -# y = "foo" -# -# when main is -# _ -> { x: "bar", y: \{} -> y } diff --git a/examples/glue/rust-platform/src/glue.rs b/examples/glue/rust-platform/src/glue.rs deleted file mode 100644 index b0787246928..00000000000 --- a/examples/glue/rust-platform/src/glue.rs +++ /dev/null @@ -1,744 +0,0 @@ -// ⚠️ GENERATED CODE ⚠️ - this entire file was generated by the `roc glue` CLI command - -#![allow(unused_unsafe)] -#![allow(unused_variables)] -#![allow(dead_code)] -#![allow(unused_mut)] -#![allow(non_snake_case)] -#![allow(non_camel_case_types)] -#![allow(non_upper_case_globals)] -#![allow(clippy::undocumented_unsafe_blocks)] -#![allow(clippy::redundant_static_lifetimes)] -#![allow(clippy::unused_unit)] -#![allow(clippy::missing_safety_doc)] -#![allow(clippy::let_and_return)] -#![allow(clippy::missing_safety_doc)] -#![allow(clippy::redundant_static_lifetimes)] -#![allow(clippy::needless_borrow)] -#![allow(clippy::clone_on_copy)] - -type Op_StderrWrite = roc_std::RocStr; -type Op_StdoutWrite = roc_std::RocStr; -type TODO_roc_function_69 = roc_std::RocStr; -type TODO_roc_function_70 = roc_std::RocStr; - -#[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" -))] -#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)] -#[repr(u8)] -pub enum discriminant_Op { - Done = 0, - StderrWrite = 1, - StdoutWrite = 2, -} - -impl core::fmt::Debug for discriminant_Op { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - Self::Done => f.write_str("discriminant_Op::Done"), - Self::StderrWrite => f.write_str("discriminant_Op::StderrWrite"), - Self::StdoutWrite => f.write_str("discriminant_Op::StdoutWrite"), - } - } -} - -#[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" -))] -#[repr(transparent)] -pub struct Op { - pointer: *mut union_Op, -} - -#[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" -))] -#[repr(C)] -union union_Op { - StderrWrite: core::mem::ManuallyDrop, - StdoutWrite: core::mem::ManuallyDrop, - _sizer: [u8; 8], -} - -#[cfg(any( - target_arch = "arm", - target_arch = "arm", - target_arch = "aarch64", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86", - target_arch = "x86_64", - target_arch = "x86_64" -))] -//TODO HAS CLOSURE 2 -#[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" -))] -#[repr(C)] -pub struct RocFunction_66 { - pub closure_data: roc_std::RocList, -} - -impl RocFunction_66 { - pub fn force_thunk(mut self, arg_0: ()) -> Op { - extern "C" { - fn roc__main_for_host_0_caller(arg_0: &(), closure_data: *mut u8, output: *mut Op); - } - - let mut output = std::mem::MaybeUninit::uninit(); - let ptr = self.closure_data.as_mut_ptr(); - unsafe { roc__main_for_host_0_caller(&arg_0, ptr, output.as_mut_ptr()) }; - unsafe { output.assume_init() } - } -} - -#[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" -))] -#[repr(C)] -pub struct RocFunction_67 { - pub closure_data: roc_std::RocList, -} - -impl RocFunction_67 { - pub fn force_thunk(mut self, arg_0: ()) -> Op { - extern "C" { - fn roc__main_for_host_1_caller(arg_0: &(), closure_data: *mut u8, output: *mut Op); - } - - let mut output = std::mem::MaybeUninit::uninit(); - let ptr = self.closure_data.as_mut_ptr(); - unsafe { roc__main_for_host_1_caller(&arg_0, ptr, output.as_mut_ptr()) }; - unsafe { output.assume_init() } - } -} - -impl Op { - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - #[inline(always)] - fn storage(&self) -> Option<&core::cell::Cell> { - let mask = match std::mem::size_of::() { - 4 => 0b11, - 8 => 0b111, - _ => unreachable!(), - }; - - // NOTE: pointer provenance is probably lost here - let unmasked_address = (self.pointer as usize) & !mask; - let untagged = unmasked_address as *const core::cell::Cell; - - if untagged.is_null() { - None - } else { - unsafe { Some(&*untagged.sub(1)) } - } - } - - #[cfg(any(target_arch = "arm", target_arch = "wasm32", target_arch = "x86"))] - /// Returns which variant this tag union holds. Note that this never includes a payload! - pub fn discriminant(&self) -> discriminant_Op { - // The discriminant is stored in the unused bytes at the end of the recursive pointer - unsafe { core::mem::transmute::((self.pointer as u8) & 0b11) } - } - - #[cfg(any(target_arch = "arm", target_arch = "wasm32", target_arch = "x86"))] - /// Internal helper - fn tag_discriminant(pointer: *mut union_Op, discriminant: discriminant_Op) -> *mut union_Op { - // The discriminant is stored in the unused bytes at the end of the union pointer - let untagged = (pointer as usize) & (!0b11 as usize); - let tagged = untagged | (discriminant as usize); - - tagged as *mut union_Op - } - - #[cfg(any(target_arch = "arm", target_arch = "wasm32", target_arch = "x86"))] - /// Internal helper - fn union_pointer(&self) -> *mut union_Op { - // The discriminant is stored in the unused bytes at the end of the union pointer - ((self.pointer as usize) & (!0b11 as usize)) as *mut union_Op - } - - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - /// A tag named Done, which has no payload. - pub const Done: Self = Self { - pointer: core::ptr::null_mut(), - }; - - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - /// Unsafely assume this `Op` has a `.discriminant()` of `StderrWrite` and return its payload at index 0. - /// (Always examine `.discriminant()` first to make sure this is the correct variant!) - /// Panics in debug builds if the `.discriminant()` doesn't return `StderrWrite`. - pub unsafe fn get_StderrWrite_0(&self) -> roc_std::RocStr { - debug_assert_eq!(self.discriminant(), discriminant_Op::StderrWrite); - - extern "C" { - #[link_name = "roc__getter__2_generic"] - fn getter(_: *mut roc_std::RocStr, _: *const Op); - } - - let mut ret = core::mem::MaybeUninit::uninit(); - getter(ret.as_mut_ptr(), self); - ret.assume_init() - } - - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - /// Unsafely assume this `Op` has a `.discriminant()` of `StderrWrite` and return its payload at index 1. - /// (Always examine `.discriminant()` first to make sure this is the correct variant!) - /// Panics in debug builds if the `.discriminant()` doesn't return `StderrWrite`. - pub unsafe fn get_StderrWrite_1(&self) -> RocFunction_67 { - debug_assert_eq!(self.discriminant(), discriminant_Op::StderrWrite); - - extern "C" { - #[link_name = "roc__getter__3_size"] - fn size() -> usize; - - #[link_name = "roc__getter__3_generic"] - fn getter(_: *mut u8, _: *const Op); - } - - // allocate memory to store this variably-sized value - // allocates with roc_alloc, but that likely still uses the heap - let it = std::iter::repeat(0xAAu8).take(size()); - let mut bytes = roc_std::RocList::from_iter(it); - - getter(bytes.as_mut_ptr(), self); - - RocFunction_67 { - closure_data: bytes, - } - } - - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - /// Construct a tag named `StderrWrite`, with the appropriate payload - pub fn StderrWrite(arg: Op_StderrWrite) -> Self { - let size = core::mem::size_of::(); - let align = core::mem::align_of::() as u32; - - unsafe { - let ptr = roc_std::roc_alloc_refcounted::(); - - *ptr = union_Op { - StderrWrite: core::mem::ManuallyDrop::new(arg), - }; - - Self { - pointer: Self::tag_discriminant(ptr, discriminant_Op::StderrWrite), - } - } - } - - #[cfg(any(target_arch = "arm", target_arch = "wasm32", target_arch = "x86"))] - /// Unsafely assume this `Op` has a `.discriminant()` of `StderrWrite` and convert it to `StderrWrite`'s payload. - /// (Always examine `.discriminant()` first to make sure this is the correct variant!) - /// Panics in debug builds if the `.discriminant()` doesn't return `StderrWrite`. - pub unsafe fn into_StderrWrite(mut self) -> Op_StderrWrite { - debug_assert_eq!(self.discriminant(), discriminant_Op::StderrWrite); - let payload = { - let ptr = (self.pointer as usize & !0b11) as *mut union_Op; - let mut uninitialized = core::mem::MaybeUninit::uninit(); - let swapped = unsafe { - core::mem::replace( - &mut (*ptr).StderrWrite, - core::mem::ManuallyDrop::new(uninitialized.assume_init()), - ) - }; - - core::mem::forget(self); - - core::mem::ManuallyDrop::into_inner(swapped) - }; - - payload - } - - #[cfg(any(target_arch = "arm", target_arch = "wasm32", target_arch = "x86"))] - /// Unsafely assume this `Op` has a `.discriminant()` of `StderrWrite` and return its payload. - /// (Always examine `.discriminant()` first to make sure this is the correct variant!) - /// Panics in debug builds if the `.discriminant()` doesn't return `StderrWrite`. - pub unsafe fn as_StderrWrite(&self) -> &Op_StderrWrite { - debug_assert_eq!(self.discriminant(), discriminant_Op::StderrWrite); - let payload = { - let ptr = (self.pointer as usize & !0b11) as *mut union_Op; - - unsafe { &(*ptr).StderrWrite } - }; - - &payload - } - - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - /// Unsafely assume this `Op` has a `.discriminant()` of `StdoutWrite` and return its payload at index 0. - /// (Always examine `.discriminant()` first to make sure this is the correct variant!) - /// Panics in debug builds if the `.discriminant()` doesn't return `StdoutWrite`. - pub unsafe fn get_StdoutWrite_0(&self) -> roc_std::RocStr { - debug_assert_eq!(self.discriminant(), discriminant_Op::StdoutWrite); - - extern "C" { - #[link_name = "roc__getter__2_generic"] - fn getter(_: *mut roc_std::RocStr, _: *const Op); - } - - let mut ret = core::mem::MaybeUninit::uninit(); - getter(ret.as_mut_ptr(), self); - ret.assume_init() - } - - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - /// Unsafely assume this `Op` has a `.discriminant()` of `StdoutWrite` and return its payload at index 1. - /// (Always examine `.discriminant()` first to make sure this is the correct variant!) - /// Panics in debug builds if the `.discriminant()` doesn't return `StdoutWrite`. - pub unsafe fn get_StdoutWrite_1(&self) -> RocFunction_66 { - debug_assert_eq!(self.discriminant(), discriminant_Op::StdoutWrite); - - extern "C" { - #[link_name = "roc__getter__3_size"] - fn size() -> usize; - - #[link_name = "roc__getter__3_generic"] - fn getter(_: *mut u8, _: *const Op); - } - - // allocate memory to store this variably-sized value - // allocates with roc_alloc, but that likely still uses the heap - let it = std::iter::repeat(0xAAu8).take(size()); - let mut bytes = roc_std::RocList::from_iter(it); - - getter(bytes.as_mut_ptr(), self); - - RocFunction_66 { - closure_data: bytes, - } - } - - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - /// Construct a tag named `StdoutWrite`, with the appropriate payload - pub fn StdoutWrite(arg: Op_StdoutWrite) -> Self { - let size = core::mem::size_of::(); - let align = core::mem::align_of::() as u32; - - unsafe { - let ptr = roc_std::roc_alloc_refcounted::(); - - *ptr = union_Op { - StdoutWrite: core::mem::ManuallyDrop::new(arg), - }; - - Self { - pointer: Self::tag_discriminant(ptr, discriminant_Op::StdoutWrite), - } - } - } - - #[cfg(any(target_arch = "arm", target_arch = "wasm32", target_arch = "x86"))] - /// Unsafely assume this `Op` has a `.discriminant()` of `StdoutWrite` and convert it to `StdoutWrite`'s payload. - /// (Always examine `.discriminant()` first to make sure this is the correct variant!) - /// Panics in debug builds if the `.discriminant()` doesn't return `StdoutWrite`. - pub unsafe fn into_StdoutWrite(mut self) -> Op_StdoutWrite { - debug_assert_eq!(self.discriminant(), discriminant_Op::StdoutWrite); - let payload = { - let ptr = (self.pointer as usize & !0b11) as *mut union_Op; - let mut uninitialized = core::mem::MaybeUninit::uninit(); - let swapped = unsafe { - core::mem::replace( - &mut (*ptr).StdoutWrite, - core::mem::ManuallyDrop::new(uninitialized.assume_init()), - ) - }; - - core::mem::forget(self); - - core::mem::ManuallyDrop::into_inner(swapped) - }; - - payload - } - - #[cfg(any(target_arch = "arm", target_arch = "wasm32", target_arch = "x86"))] - /// Unsafely assume this `Op` has a `.discriminant()` of `StdoutWrite` and return its payload. - /// (Always examine `.discriminant()` first to make sure this is the correct variant!) - /// Panics in debug builds if the `.discriminant()` doesn't return `StdoutWrite`. - pub unsafe fn as_StdoutWrite(&self) -> &Op_StdoutWrite { - debug_assert_eq!(self.discriminant(), discriminant_Op::StdoutWrite); - let payload = { - let ptr = (self.pointer as usize & !0b11) as *mut union_Op; - - unsafe { &(*ptr).StdoutWrite } - }; - - &payload - } - - #[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] - /// Returns which variant this tag union holds. Note that this never includes a payload! - pub fn discriminant(&self) -> discriminant_Op { - // The discriminant is stored in the unused bytes at the end of the recursive pointer - unsafe { core::mem::transmute::((self.pointer as u8) & 0b111) } - } - - #[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] - /// Internal helper - fn tag_discriminant(pointer: *mut union_Op, discriminant: discriminant_Op) -> *mut union_Op { - // The discriminant is stored in the unused bytes at the end of the union pointer - let untagged = (pointer as usize) & (!0b111 as usize); - let tagged = untagged | (discriminant as usize); - - tagged as *mut union_Op - } - - #[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] - /// Internal helper - fn union_pointer(&self) -> *mut union_Op { - // The discriminant is stored in the unused bytes at the end of the union pointer - ((self.pointer as usize) & (!0b111 as usize)) as *mut union_Op - } - - #[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] - /// Unsafely assume this `Op` has a `.discriminant()` of `StderrWrite` and convert it to `StderrWrite`'s payload. - /// (Always examine `.discriminant()` first to make sure this is the correct variant!) - /// Panics in debug builds if the `.discriminant()` doesn't return `StderrWrite`. - pub unsafe fn into_StderrWrite(mut self) -> Op_StderrWrite { - debug_assert_eq!(self.discriminant(), discriminant_Op::StderrWrite); - let payload = { - let ptr = (self.pointer as usize & !0b111) as *mut union_Op; - let mut uninitialized = core::mem::MaybeUninit::uninit(); - let swapped = unsafe { - core::mem::replace( - &mut (*ptr).StderrWrite, - core::mem::ManuallyDrop::new(uninitialized.assume_init()), - ) - }; - - core::mem::forget(self); - - core::mem::ManuallyDrop::into_inner(swapped) - }; - - payload - } - - #[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] - /// Unsafely assume this `Op` has a `.discriminant()` of `StderrWrite` and return its payload. - /// (Always examine `.discriminant()` first to make sure this is the correct variant!) - /// Panics in debug builds if the `.discriminant()` doesn't return `StderrWrite`. - pub unsafe fn as_StderrWrite(&self) -> &Op_StderrWrite { - debug_assert_eq!(self.discriminant(), discriminant_Op::StderrWrite); - let payload = { - let ptr = (self.pointer as usize & !0b111) as *mut union_Op; - - unsafe { &(*ptr).StderrWrite } - }; - - &payload - } - - #[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] - /// Unsafely assume this `Op` has a `.discriminant()` of `StdoutWrite` and convert it to `StdoutWrite`'s payload. - /// (Always examine `.discriminant()` first to make sure this is the correct variant!) - /// Panics in debug builds if the `.discriminant()` doesn't return `StdoutWrite`. - pub unsafe fn into_StdoutWrite(mut self) -> Op_StdoutWrite { - debug_assert_eq!(self.discriminant(), discriminant_Op::StdoutWrite); - let payload = { - let ptr = (self.pointer as usize & !0b111) as *mut union_Op; - let mut uninitialized = core::mem::MaybeUninit::uninit(); - let swapped = unsafe { - core::mem::replace( - &mut (*ptr).StdoutWrite, - core::mem::ManuallyDrop::new(uninitialized.assume_init()), - ) - }; - - core::mem::forget(self); - - core::mem::ManuallyDrop::into_inner(swapped) - }; - - payload - } - - #[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] - /// Unsafely assume this `Op` has a `.discriminant()` of `StdoutWrite` and return its payload. - /// (Always examine `.discriminant()` first to make sure this is the correct variant!) - /// Panics in debug builds if the `.discriminant()` doesn't return `StdoutWrite`. - pub unsafe fn as_StdoutWrite(&self) -> &Op_StdoutWrite { - debug_assert_eq!(self.discriminant(), discriminant_Op::StdoutWrite); - let payload = { - let ptr = (self.pointer as usize & !0b111) as *mut union_Op; - - unsafe { &(*ptr).StdoutWrite } - }; - - &payload - } -} - -impl Drop for Op { - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - fn drop(&mut self) { - // We only need to do any work if there's actually a heap-allocated payload. - if let Some(storage) = self.storage() { - let mut new_storage = storage.get(); - - // Decrement the refcount - let needs_dealloc = !new_storage.is_readonly() && new_storage.decrease(); - - if needs_dealloc { - // Drop the payload first. - match self.discriminant() { - discriminant_Op::Done => {} - discriminant_Op::StderrWrite => unsafe { - core::mem::ManuallyDrop::drop(&mut (&mut *self.union_pointer()).StderrWrite) - }, - discriminant_Op::StdoutWrite => unsafe { - core::mem::ManuallyDrop::drop(&mut (&mut *self.union_pointer()).StdoutWrite) - }, - } - - // Dealloc the pointer - let alignment = - core::mem::align_of::().max(core::mem::align_of::()); - - unsafe { - crate::roc_dealloc(storage.as_ptr().cast(), alignment as u32); - } - } else { - // Write the storage back. - storage.set(new_storage); - } - } - } -} - -impl Eq for Op {} - -impl PartialEq for Op { - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - fn eq(&self, other: &Self) -> bool { - if self.discriminant() != other.discriminant() { - return false; - } - - unsafe { - match self.discriminant() { - discriminant_Op::Done => true, - discriminant_Op::StderrWrite => { - (&*self.union_pointer()).StderrWrite == (&*other.union_pointer()).StderrWrite - } - discriminant_Op::StdoutWrite => { - (&*self.union_pointer()).StdoutWrite == (&*other.union_pointer()).StdoutWrite - } - } - } - } -} - -impl PartialOrd for Op { - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - fn partial_cmp(&self, other: &Self) -> Option { - match self.discriminant().partial_cmp(&other.discriminant()) { - Some(core::cmp::Ordering::Equal) => {} - not_eq => return not_eq, - } - - unsafe { - match self.discriminant() { - discriminant_Op::Done => Some(core::cmp::Ordering::Equal), - discriminant_Op::StderrWrite => (&*self.union_pointer()) - .StderrWrite - .partial_cmp(&(&*other.union_pointer()).StderrWrite), - discriminant_Op::StdoutWrite => (&*self.union_pointer()) - .StdoutWrite - .partial_cmp(&(&*other.union_pointer()).StdoutWrite), - } - } - } -} - -impl Ord for Op { - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - match self.discriminant().cmp(&other.discriminant()) { - core::cmp::Ordering::Equal => {} - not_eq => return not_eq, - } - - unsafe { - match self.discriminant() { - discriminant_Op::Done => core::cmp::Ordering::Equal, - discriminant_Op::StderrWrite => (&*self.union_pointer()) - .StderrWrite - .cmp(&(&*other.union_pointer()).StderrWrite), - discriminant_Op::StdoutWrite => (&*self.union_pointer()) - .StdoutWrite - .cmp(&(&*other.union_pointer()).StdoutWrite), - } - } - } -} - -impl Clone for Op { - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - fn clone(&self) -> Self { - if let Some(storage) = self.storage() { - let mut new_storage = storage.get(); - if !new_storage.is_readonly() { - new_storage.increment_reference_count(); - storage.set(new_storage); - } - } - - Self { - pointer: self.pointer, - } - } -} - -impl core::hash::Hash for Op { - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - fn hash(&self, state: &mut H) { - match self.discriminant() { - discriminant_Op::Done => discriminant_Op::Done.hash(state), - discriminant_Op::StderrWrite => unsafe { - discriminant_Op::StderrWrite.hash(state); - (&*self.union_pointer()).StderrWrite.hash(state); - }, - discriminant_Op::StdoutWrite => unsafe { - discriminant_Op::StdoutWrite.hash(state); - (&*self.union_pointer()).StdoutWrite.hash(state); - }, - } - } -} - -impl core::fmt::Debug for Op { - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.write_str("Op::")?; - - unsafe { - match self.discriminant() { - discriminant_Op::Done => f.write_str("Done"), - discriminant_Op::StderrWrite => f - .debug_tuple("StderrWrite") - // TODO HAS CLOSURE - .finish(), - discriminant_Op::StdoutWrite => f - .debug_tuple("StdoutWrite") - // TODO HAS CLOSURE - .finish(), - } - } - } -} diff --git a/examples/glue/rust-platform/src/lib.rs b/examples/glue/rust-platform/src/lib.rs deleted file mode 100644 index 64bf071a65f..00000000000 --- a/examples/glue/rust-platform/src/lib.rs +++ /dev/null @@ -1,123 +0,0 @@ -#![allow(non_snake_case)] - -use core::ffi::c_void; -use roc_app::Op; -use roc_std::RocStr; -use std::ffi::CStr; -use std::io::Write; -use std::os::raw::c_char; - -use roc_app::main_for_host as roc_main; - -#[no_mangle] -pub unsafe extern "C" fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void { - return libc::malloc(size); -} - -#[no_mangle] -pub unsafe extern "C" fn roc_realloc( - c_ptr: *mut c_void, - new_size: usize, - _old_size: usize, - _alignment: u32, -) -> *mut c_void { - return libc::realloc(c_ptr, new_size); -} - -#[no_mangle] -pub unsafe extern "C" fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) { - return libc::free(c_ptr); -} - -#[no_mangle] -pub unsafe extern "C" fn roc_panic(msg: *mut RocStr, tag_id: u32) { - match tag_id { - 0 => { - eprintln!("Roc standard library hit a panic: {}", &*msg); - } - 1 => { - eprintln!("Application hit a panic: {}", &*msg); - } - _ => unreachable!(), - } - std::process::exit(1); -} - -#[no_mangle] -pub unsafe extern "C" fn roc_dbg(loc: *mut RocStr, msg: *mut RocStr, src: *mut RocStr) { - eprintln!("[{}] {} = {}", &*loc, &*src, &*msg); -} - -#[no_mangle] -pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { - libc::memset(dst, c, n) -} - -#[cfg(unix)] -#[no_mangle] -pub unsafe extern "C" fn roc_getppid() -> libc::pid_t { - libc::getppid() -} - -#[cfg(unix)] -#[no_mangle] -pub unsafe extern "C" fn roc_mmap( - addr: *mut libc::c_void, - len: libc::size_t, - prot: libc::c_int, - flags: libc::c_int, - fd: libc::c_int, - offset: libc::off_t, -) -> *mut libc::c_void { - libc::mmap(addr, len, prot, flags, fd, offset) -} - -#[cfg(unix)] -#[no_mangle] -pub unsafe extern "C" fn roc_shm_open( - name: *const libc::c_char, - oflag: libc::c_int, - mode: libc::mode_t, -) -> libc::c_int { - libc::shm_open(name, oflag, mode as libc::c_uint) -} - -#[no_mangle] -pub extern "C" fn rust_main() -> i32 { - use roc_app::discriminant_Op::*; - - println!("Let's do things!"); - - let mut op: Op = roc_main(); - - loop { - match op.discriminant() { - StdoutWrite => { - let stdout_write = op.get_StdoutWrite(); - let output: RocStr = stdout_write.f0; - op = unsafe { stdout_write.f1.force_thunk() }; - - if let Err(e) = std::io::stdout().write_all(output.as_bytes()) { - panic!("Writing to stdout failed! {:?}", e); - } - } - StderrWrite => { - let stderr_write = op.get_StderrWrite(); - let output: RocStr = stderr_write.f0; - op = unsafe { stderr_write.f1.force_thunk() }; - - if let Err(e) = std::io::stderr().write_all(output.as_bytes()) { - panic!("Writing to stdout failed! {:?}", e); - } - } - Done => { - break; - } - } - } - - println!("Done!"); - - // Exit code - 0 -} diff --git a/examples/jvm-interop/bridge.c b/examples/jvm-interop/bridge.c deleted file mode 100644 index 0e2a9827532..00000000000 --- a/examples/jvm-interop/bridge.c +++ /dev/null @@ -1,384 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "javaSource_Demo.h" - -JavaVM* vm; - -#define ERR_MSG_MAX_SIZE 256 - -jmp_buf exception_buffer; -char* err_msg[ERR_MSG_MAX_SIZE] = {0}; - -jint JNI_OnLoad(JavaVM *loadedVM, void *reserved) -{ - // https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/invocation.html - vm = loadedVM; - return JNI_VERSION_1_2; -} - -void *roc_alloc(size_t size, unsigned int alignment) -{ - return malloc(size); -} - -void *roc_realloc(void *ptr, size_t new_size, size_t old_size, - unsigned int alignment) -{ - return realloc(ptr, new_size); -} - -void roc_dealloc(void *ptr, unsigned int alignment) -{ - free(ptr); -} - -void *roc_memset(void *str, int c, size_t n) -{ - return memset(str, c, n); -} - -// Reference counting - -// If the refcount is set to this, that means the allocation is -// stored in readonly memory in the binary, and we must not -// attempt to increment or decrement it; if we do, we'll segfault! -const ssize_t REFCOUNT_READONLY = 0; -const ssize_t REFCOUNT_ONE = (ssize_t)PTRDIFF_MIN; -const size_t MASK = (size_t)PTRDIFF_MIN; - -// Increment reference count, given a pointer to the first element in a collection. -// We don't need to check for overflow because in order to overflow a usize worth of refcounts, -// you'd need to somehow have more pointers in memory than the OS's virtual address space can hold. -void incref(uint8_t* bytes, uint32_t alignment) -{ - ssize_t *refcount_ptr = ((ssize_t *)bytes) - 1; - ssize_t refcount = *refcount_ptr; - - if (refcount != REFCOUNT_READONLY) { - *refcount_ptr = refcount + 1; - } -} - -// Decrement reference count, given a pointer to the first element in a collection. -// Then call roc_dealloc if nothing is referencing this collection anymore. -void decref(uint8_t* bytes, uint32_t alignment) -{ - if (bytes == NULL) { - return; - } - - size_t extra_bytes = (sizeof(size_t) >= (size_t)alignment) ? sizeof(size_t) : (size_t)alignment; - ssize_t *refcount_ptr = ((ssize_t *)bytes) - 1; - ssize_t refcount = *refcount_ptr; - - if (refcount != REFCOUNT_READONLY) { - *refcount_ptr = refcount - 1; - - if (refcount == REFCOUNT_ONE) { - void *original_allocation = (void *)(refcount_ptr - (extra_bytes - sizeof(size_t))); - - roc_dealloc(original_allocation, alignment); - } - } -} - -struct RocListI32 -{ - int32_t *bytes; - size_t len; - size_t capacity; -}; - -struct RocListI32 init_roclist_i32(int32_t *bytes, size_t len) -{ - if (len == 0) - { - struct RocListI32 ret = { - .len = 0, - .bytes = NULL, - .capacity = 0, - }; - - return ret; - } - else - { - size_t refcount_size = sizeof(size_t); - ssize_t* data = (ssize_t*)roc_alloc(len + refcount_size, alignof(size_t)); - data[0] = REFCOUNT_ONE; - int32_t *new_content = (int32_t *)(data + 1); - - struct RocListI32 ret; - - memcpy(new_content, bytes, len * sizeof(int32_t)); - - ret.bytes = new_content; - ret.len = len; - ret.capacity = len; - - return ret; - } -} -// RocListU8 (List U8) - -struct RocListU8 -{ - uint8_t *bytes; - size_t len; - size_t capacity; -}; - -struct RocListU8 init_roclist_u8(uint8_t *bytes, size_t len) -{ - if (len == 0) - { - struct RocListU8 ret = { - .len = 0, - .bytes = NULL, - .capacity = 0, - }; - - return ret; - } - else - { - - size_t refcount_size = sizeof(size_t); - ssize_t* data = (ssize_t*)roc_alloc(len + refcount_size, alignof(size_t)); - data[0] = REFCOUNT_ONE; - uint8_t *new_content = (uint8_t *)(data + 1); - - struct RocListU8 ret; - - memcpy(new_content, bytes, len * sizeof(uint8_t)); - - ret.bytes = new_content; - ret.len = len; - ret.capacity = len; - - return ret; - } -} - -// RocStr - -struct RocStr -{ - uint8_t *bytes; - size_t len; - size_t capacity; -}; - -struct RocStr init_rocstr(uint8_t *bytes, size_t len) -{ - if (len < sizeof(struct RocStr)) - { - // Start out with zeroed memory, so that - // if we end up comparing two small RocStr values - // for equality, we won't risk memory garbage resulting - // in two equal strings appearing unequal. - struct RocStr ret = { - .len = 0, - .bytes = NULL, - .capacity = 0, - }; - - // Copy the bytes into the stack allocation - memcpy(&ret, bytes, len); - - // Record the string's len in the last byte of the stack allocation - ((uint8_t *)&ret)[sizeof(struct RocStr) - 1] = (uint8_t)len | 0b10000000; - - return ret; - } - else - { - // A large RocStr is the same as a List U8 (aka RocListU8) in memory. - struct RocListU8 roc_bytes = init_roclist_u8(bytes, len); - - struct RocStr ret = { - .len = roc_bytes.len, - .bytes = roc_bytes.bytes, - .capacity = roc_bytes.capacity, - }; - - return ret; - } -} - -bool is_small_str(struct RocStr str) -{ - return ((ssize_t)str.capacity) < 0; -} - -bool is_seamless_str_slice(struct RocStr str) -{ - return ((ssize_t)str.len) < 0; -} - -bool is_seamless_listi32_slice(struct RocListI32 list) -{ - return ((ssize_t)list.capacity) < 0; -} - -// Determine the len of the string, taking into -// account the small string optimization -size_t roc_str_len(struct RocStr str) -{ - uint8_t *bytes = (uint8_t *)&str; - uint8_t last_byte = bytes[sizeof(str) - 1]; - uint8_t last_byte_xored = last_byte ^ 0b10000000; - size_t small_len = (size_t)(last_byte_xored); - size_t big_len = str.len; - - // Avoid branch misprediction costs by always - // determining both small_len and big_len, - // so this compiles to a cmov instruction. - if (is_small_str(str)) - { - return small_len; - } - else - { - return big_len; - } -} - -__attribute__((noreturn)) void roc_panic(struct RocStr *msg, unsigned int tag_id) -{ - char* bytes = is_small_str(*msg) ? (char*)msg : (char*)msg->bytes; - const size_t str_len = roc_str_len(*msg); - - int len = str_len > ERR_MSG_MAX_SIZE ? ERR_MSG_MAX_SIZE : str_len; - strncpy((char*)err_msg, bytes, len); - - // Free the underlying allocation if needed. - if (!is_small_str(*msg)) { - if (is_seamless_str_slice(*msg)){ - decref((uint8_t *)(msg->capacity << 1), alignof(uint8_t *)); - } - else { - decref(msg->bytes, alignof(uint8_t *)); - } - } - - longjmp(exception_buffer, 1); -} - -void roc_dbg(struct RocStr *loc, struct RocStr *msg, struct RocStr *src) { - char* loc_bytes = is_small_str(*loc) ? (char*)loc : (char*)loc->bytes; - char* src_bytes = is_small_str(*src) ? (char*)src : (char*)src->bytes; - char* msg_bytes = is_small_str(*msg) ? (char*)msg : (char*)msg->bytes; - fprintf(stderr, "[%s] %s = %s\n", loc_bytes, src_bytes, msg_bytes); -} - -extern void roc__program_for_host_1__InterpolateString_caller(struct RocStr *name, char *closure_data, struct RocStr *ret); - -extern void roc__program_for_host_1__MulArrByScalar_caller(struct RocListI32 *arr, int32_t *scalar, char *closure_data, struct RocListI32 *ret); - -extern void roc__program_for_host_1__Factorial_caller(int64_t *scalar, char *closure_data, int64_t *ret); - - -JNIEXPORT jstring JNICALL Java_javaSource_Demo_sayHello - (JNIEnv *env, jobject thisObj, jstring name) -{ - const char *jnameChars = (*env)->GetStringUTFChars(env, name, 0); - // we copy just in case the jvm would try to reclaim that mem - uint8_t *cnameChars = (uint8_t *)strdup(jnameChars); - size_t nameLength = (size_t) (*env)->GetStringLength(env, name); - (*env)->ReleaseStringUTFChars(env, name, jnameChars); - - - struct RocStr rocName = init_rocstr(cnameChars, nameLength); - struct RocStr ret = {0}; - - // Call the Roc function to populate `ret`'s bytes. - roc__program_for_host_1__InterpolateString_caller(&rocName, 0, &ret); - jbyte *bytes = (jbyte*)(is_small_str(ret) ? (uint8_t*)&ret : ret.bytes); - - // java being java making this a lot harder than it needs to be - // https://stackoverflow.com/questions/32205446/getting-true-utf-8-characters-in-java-jni - // https://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/types.html#wp16542 - // but as i refuse converting those manually to their correct form, we just let the jvm handle the conversion - // by first making a java byte array then converting the byte array to our final jstring - jbyteArray byteArray = (*env)->NewByteArray(env, ret.len); - (*env)->SetByteArrayRegion(env, byteArray, 0, ret.len, bytes); - - jstring charsetName = (*env)->NewStringUTF(env, "UTF-8"); - jclass stringClass = (*env)->FindClass(env, "java/lang/String"); - // https://docs.oracle.com/javase/7/docs/jdk/api/jpda/jdi/com/sun/jdi/doc-files/signature.html - jmethodID stringConstructor = (*env)->GetMethodID(env, stringClass, "", "([BLjava/lang/String;)V"); - jstring result = (*env)->NewObject(env, stringClass, stringConstructor, byteArray, charsetName); - - // cleanup - if (!is_seamless_str_slice(ret)) { - decref(ret.bytes, alignof(uint8_t *)); - } - - (*env)->DeleteLocalRef(env, charsetName); - (*env)->DeleteLocalRef(env, byteArray); - - free(cnameChars); - - return result; -} - - -JNIEXPORT jintArray JNICALL Java_javaSource_Demo_mulArrByScalar - (JNIEnv *env, jobject thisObj, jintArray arr, jint scalar) -{ - // extract data from jvm types - jint* jarr = (*env)->GetIntArrayElements(env, arr, NULL); - jsize len = (*env)->GetArrayLength(env, arr); - - // pass data to platform - struct RocListI32 originalArray = init_roclist_i32(jarr, len); - incref((void *)&originalArray, alignof(int32_t*)); - struct RocListI32 ret = {0}; - - roc__program_for_host_1__MulArrByScalar_caller(&originalArray, &scalar, 0, &ret); - - // create jvm constructs - jintArray multiplied = (*env)->NewIntArray(env, ret.len); - (*env)->SetIntArrayRegion(env, multiplied, 0, ret.len, (jint*) ret.bytes); - - // cleanup - (*env)->ReleaseIntArrayElements(env, arr, jarr, 0); - - if (is_seamless_listi32_slice(ret)) { - decref((void *)(ret.capacity << 1), alignof(uint8_t *)); - } - else { - decref((void *)ret.bytes, alignof(uint8_t *)); - } - - return multiplied; -} - -JNIEXPORT jlong JNICALL Java_javaSource_Demo_factorial - (JNIEnv *env, jobject thisObj, jlong num) -{ - int64_t ret; - // can crash - meaning call roc_panic, so we set a jump here - if (setjmp(exception_buffer)) { - // exception was thrown, handle it - jclass exClass = (*env)->FindClass(env, "java/lang/RuntimeException"); - const char *msg = (const char *)err_msg; - return (*env)->ThrowNew(env, exClass, msg); - } - else { - int64_t n = (int64_t)num; - roc__program_for_host_1__Factorial_caller(&n, 0, &ret); - return ret; - } -} diff --git a/examples/jvm-interop/impl.roc b/examples/jvm-interop/impl.roc deleted file mode 100644 index 5b72fc724fa..00000000000 --- a/examples/jvm-interop/impl.roc +++ /dev/null @@ -1,23 +0,0 @@ -app [program] { pf: platform "platform.roc" } - -interpolate_string : Str -> Str -interpolate_string = \name -> - "Hello from Roc $(name)!!!🤘🤘🤘" - -# jint is i32 -mul_arr_by_scalar : List I32, I32 -> List I32 -mul_arr_by_scalar = \arr, scalar -> - List.map(arr, \x -> x * scalar) - -# java doesn't have unsigned numbers so we cope with long -# factorial : I64 -> I64 -factorial = \n -> - if n < 0 then - # while we get the chance, exemplify a roc panic in an interop - crash("No negatives here!!!") - else if n == 0 then - 1 - else - n * (factorial((n - 1))) - -program = { interpolate_string, factorial, mul_arr_by_scalar } diff --git a/examples/jvm-interop/platform.roc b/examples/jvm-interop/platform.roc deleted file mode 100644 index 933b0243e16..00000000000 --- a/examples/jvm-interop/platform.roc +++ /dev/null @@ -1,13 +0,0 @@ -platform "jvm-interop" - requires {} { program : _ } - exposes [] - packages {} - imports [] - provides [program_for_host] - -program_for_host : { - interpolate_string : (Str -> Str) as InterpolateString, - mul_arr_by_scalar : (List I32, I32 -> List I32) as MulArrByScalar, - factorial : (I64 -> I64) as Factorial, -} -program_for_host = program diff --git a/examples/nodejs-interop/native-c-api/demo.c b/examples/nodejs-interop/native-c-api/demo.c deleted file mode 100644 index a46462dbde4..00000000000 --- a/examples/nodejs-interop/native-c-api/demo.c +++ /dev/null @@ -1,418 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -napi_env napi_global_env; - -void *roc_alloc(size_t size, unsigned int alignment) { return malloc(size); } - -void *roc_realloc(void *ptr, size_t new_size, size_t old_size, - unsigned int alignment) -{ - return realloc(ptr, new_size); -} - -void roc_dealloc(void *ptr, unsigned int alignment) { free(ptr); } - -void roc_panic(void *ptr, unsigned int alignment) -{ - // WARNING: If roc_panic is called before napi_global_env is set, - // the result will be undefined behavior. So never call any Roc - // functions before setting napi_global_env! - napi_throw_error(napi_global_env, NULL, (char *)ptr); -} - -void roc_dbg(char* loc, char* msg, char* src) { - fprintf(stderr, "[%s] %s = %s\n", loc, src, msg); -} - -void *roc_memset(void *str, int c, size_t n) { return memset(str, c, n); } - -// Reference counting - -// If the refcount is set to this, that means the allocation is -// stored in readonly memory in the binary, and we must not -// attempt to increment or decrement it; if we do, we'll segfault! -const ssize_t REFCOUNT_READONLY = 0; -const ssize_t REFCOUNT_ONE = (ssize_t)PTRDIFF_MIN; -const size_t MASK = (size_t)PTRDIFF_MIN; - -// Increment reference count, given a pointer to the first element in a collection. -// We don't need to check for overflow because in order to overflow a usize worth of refcounts, -// you'd need to somehow have more pointers in memory than the OS's virtual address space can hold. -void incref(uint8_t* bytes, uint32_t alignment) -{ - ssize_t *refcount_ptr = ((ssize_t *)bytes) - 1; - ssize_t refcount = *refcount_ptr; - - if (refcount != REFCOUNT_READONLY) { - *refcount_ptr = refcount + 1; - } -} - -// Decrement reference count, given a pointer to the first byte of a collection's elements. -// Then call roc_dealloc if nothing is referencing this collection anymore. -void decref_heap_bytes(uint8_t* bytes, uint32_t alignment) -{ - size_t extra_bytes = (sizeof(size_t) >= (size_t)alignment) ? sizeof(size_t) : (size_t)alignment; - ssize_t *refcount_ptr = ((ssize_t *)bytes) - 1; - ssize_t refcount = *refcount_ptr; - - if (refcount != REFCOUNT_READONLY) { - *refcount_ptr = refcount - 1; - - if (refcount == REFCOUNT_ONE) { - void *original_allocation = (void *)(refcount_ptr - (extra_bytes - sizeof(size_t))); - - roc_dealloc(original_allocation, alignment); - } - } -} - -// RocBytes (List U8) - -struct RocBytes -{ - uint8_t *bytes; - size_t len; - size_t capacity; -}; - -struct RocBytes empty_rocbytes() -{ - struct RocBytes ret = { - .len = 0, - .bytes = NULL, - .capacity = 0, - }; - - return ret; -} - -struct RocBytes init_rocbytes(uint8_t *bytes, size_t len) -{ - if (len == 0) - { - return empty_rocbytes(); - } - else - { - struct RocBytes ret; - size_t refcount_size = sizeof(size_t); - uint8_t *new_refcount = (uint8_t *)roc_alloc(len + refcount_size, __alignof__(size_t)); - uint8_t *new_content = new_refcount + refcount_size; - - ((ssize_t *)new_refcount)[0] = REFCOUNT_ONE; - - memcpy(new_content, bytes, len); - - ret.bytes = new_content; - ret.len = len; - ret.capacity = len; - - return ret; - } -} - -// RocStr - -struct RocStr -{ - uint8_t *bytes; - size_t len; - size_t capacity; -}; - -struct RocStr empty_roc_str() -{ - struct RocStr ret = { - .len = 0, - .bytes = NULL, - .capacity = MASK, - }; - - return ret; -} - -// Record the small string's length in the last byte of the given stack allocation -void write_small_str_len(size_t len, struct RocStr *str) { - ((uint8_t *)str)[sizeof(struct RocStr) - 1] = (uint8_t)len | 0b10000000; -} - -struct RocStr roc_str_init_small(uint8_t *bytes, size_t len) -{ - // Start out with zeroed memory, so that - // if we end up comparing two small RocStr values - // for equality, we won't risk memory garbage resulting - // in two equal strings appearing unequal. - struct RocStr ret = empty_roc_str(); - - // Copy the bytes into the stack allocation - memcpy(&ret, bytes, len); - - write_small_str_len(len, &ret); - - return ret; -} - -struct RocStr roc_str_init_large(uint8_t *bytes, size_t len, size_t capacity) -{ - // A large RocStr is the same as a List U8 (aka RocBytes) in memory. - struct RocBytes roc_bytes = init_rocbytes(bytes, len); - - struct RocStr ret = { - .len = roc_bytes.len, - .bytes = roc_bytes.bytes, - .capacity = roc_bytes.capacity, - }; - - return ret; -} - -bool is_small_str(struct RocStr str) { return ((ssize_t)str.capacity) < 0; } - -// Determine the length of the string, taking into -// account the small string optimization -size_t roc_str_len(struct RocStr str) -{ - uint8_t *bytes = (uint8_t *)&str; - uint8_t last_byte = bytes[sizeof(str) - 1]; - uint8_t last_byte_xored = last_byte ^ 0b10000000; - size_t small_len = (size_t)(last_byte_xored); - size_t big_len = str.len & PTRDIFF_MAX; // Account for seamless slices - - // Avoid branch misprediction costs by always - // determining both small_len and big_len, - // so this compiles to a cmov instruction. - if (is_small_str(str)) - { - return small_len; - } - else - { - return big_len; - } -} - -void decref_large_str(struct RocStr str) -{ - uint8_t* bytes; - - if ((ssize_t)str.len < 0) - { - // This is a seamless slice, so the bytes are located in the capacity slot. - bytes = (uint8_t*)(str.capacity << 1); - } - else - { - bytes = str.bytes; - } - - decref_heap_bytes(bytes, __alignof__(uint8_t)); -} - - -// Turn the given Node string into a RocStr and return it -napi_status node_string_into_roc_str(napi_env env, napi_value node_string, struct RocStr *roc_str) { - size_t len; - napi_status status; - - // Passing NULL for a buffer (and size 0) will make it write the length of the string into `len`. - // https://nodejs.org/api/n-api.html#napi_get_value_string_utf8 - status = napi_get_value_string_utf8(env, node_string, NULL, 0, &len); - - if (status != napi_ok) - { - return status; - } - - // Node's "write a string into this buffer" function always writes a null terminator, - // so capacity will need to be length + 1. - // https://nodejs.org/api/n-api.html#napi_get_value_string_utf8 - size_t capacity = len + 1; - - // Create a RocStr and write it into the out param - if (capacity < sizeof(struct RocStr)) - { - // If it can fit in a small string, use the string itself as the buffer. - // First, zero out those bytes; small strings need to have zeroes for any bytes - // that are not part of the string, or else comparisons between small strings might fail. - *roc_str = empty_roc_str(); - - // This writes the actual number of bytes copied into len. Theoretically they should be the same, - // but it could be different if the buffer was somehow smaller. This way we guarantee that - // the RocStr does not present any memory garbage to the user. - status = napi_get_value_string_utf8(env, node_string, (char*)roc_str, sizeof(struct RocStr), &len); - - if (status != napi_ok) - { - return status; - } - - // We have to write the length into the buffer *after* Node copies its bytes in, - // because Node will have written a null terminator, which we may need to overwrite. - write_small_str_len(len, roc_str); - } - else - { - // capacity was too big for a small string, so make a heap allocation and write into that. - uint8_t *buf = (uint8_t*)roc_alloc(capacity, __alignof__(char)); - - // This writes the actual number of bytes copied into len. Theoretically they should be the same, - // but it could be different if the buffer was somehow smaller. This way we guarantee that - // the RocStr does not present any memory garbage to the user. - status = napi_get_value_string_utf8(env, node_string, (char*)buf, capacity, &len); - - if (status != napi_ok) - { - // Something went wrong, so free the bytes we just allocated before returning. - roc_dealloc((void *)&buf, __alignof__(char *)); - - return status; - } - - *roc_str = roc_str_init_large(buf, len, capacity); - } - - return status; -} - -// Consume the given RocStr (decrement its refcount) after creating a Node string from it. -napi_value roc_str_into_node_string(napi_env env, struct RocStr roc_str) { - bool is_small = is_small_str(roc_str); - char* roc_str_contents; - - if (is_small) - { - // In a small string, the string itself contains its contents. - roc_str_contents = (char*)&roc_str; - } - else - { - roc_str_contents = (char*)roc_str.bytes; - } - - napi_status status; - napi_value answer; - - status = napi_create_string_utf8(env, roc_str_contents, roc_str_len(roc_str), &answer); - - if (status != napi_ok) - { - answer = NULL; - } - - // Decrement the RocStr because we consumed it. - if (!is_small) - { - decref_large_str(roc_str); - } - - return answer; -} - -// Create a Node string from the given RocStr. -// Don't decrement the RocStr's refcount. (To decrement it, use roc_str_into_node_string instead.) -napi_value roc_str_as_node_string(napi_env env, struct RocStr roc_str) { - bool is_small = is_small_str(roc_str); - char* roc_str_contents; - - if (is_small) - { - // In a small string, the string itself contains its contents. - roc_str_contents = (char*)&roc_str; - } - else - { - roc_str_contents = (char*)roc_str.bytes; - } - - napi_status status; - napi_value answer; - - status = napi_create_string_utf8(env, roc_str_contents, roc_str_len(roc_str), &answer); - - if (status != napi_ok) - { - return NULL; - } - - // Do not decrement the RocStr's refcount because we did not consume it. - - return answer; -} - -extern void roc__main_for_host_1_exposed_generic(struct RocStr *ret, struct RocStr *arg); - -// Receive a string value from Node and pass it to Roc as a RocStr, then get a RocStr -// back from Roc and convert it into a Node string. -napi_value call_roc(napi_env env, napi_callback_info info) { - napi_status status; - - // roc_panic needs a napi_env in order to throw a Node exception, so we provide this - // one globally in case roc_panic gets called during the execution of our Roc function. - // - // According to the docs - https://nodejs.org/api/n-api.html#napi_env - - // it's very important that the napi_env that was passed into "the initial - // native function" is the one that's "passed to any subsequent nested Node-API calls," - // so we must override this every time we call this function (as opposed to, say, - // setting it once during init). - napi_global_env = env; - - // Get the argument passed to the Node function - size_t argc = 1; - napi_value argv[1]; - - status = napi_get_cb_info(env, info, &argc, argv, NULL, NULL); - - if (status != napi_ok) - { - return NULL; - } - - napi_value node_arg = argv[0]; - - struct RocStr roc_arg; - - status = node_string_into_roc_str(env, node_arg, &roc_arg); - - if (status != napi_ok) - { - return NULL; - } - - struct RocStr roc_ret; - // Call the Roc function to populate `roc_ret`'s bytes. - roc__main_for_host_1_exposed_generic(&roc_ret, &roc_arg); - - // Consume the RocStr to create the Node string. - return roc_str_into_node_string(env, roc_ret); -} - -napi_value init(napi_env env, napi_value exports) { - napi_status status; - napi_value fn; - - status = napi_create_function(env, NULL, 0, call_roc, NULL, &fn); - - if (status != napi_ok) - { - return NULL; - } - - status = napi_set_named_property(env, exports, "hello", fn); - - if (status != napi_ok) - { - return NULL; - } - - return exports; -} - -NAPI_MODULE(NODE_GYP_MODULE_NAME, init) diff --git a/examples/nodejs-interop/native-c-api/platform/main.roc b/examples/nodejs-interop/native-c-api/platform/main.roc deleted file mode 100644 index b633421669d..00000000000 --- a/examples/nodejs-interop/native-c-api/platform/main.roc +++ /dev/null @@ -1,10 +0,0 @@ -platform "nodejs-interop" - requires {} { main : Str -> Str } - exposes [] - packages {} - imports [] - provides [main_for_host] - -main_for_host : Str -> Str -main_for_host = \message -> - main(message) diff --git a/examples/nodejs-interop/wasm/platform/host.zig b/examples/nodejs-interop/wasm/platform/host.zig deleted file mode 100644 index 2eeab845b03..00000000000 --- a/examples/nodejs-interop/wasm/platform/host.zig +++ /dev/null @@ -1,53 +0,0 @@ -const str = @import("glue").str; -const builtin = @import("builtin"); -const RocStr = str.RocStr; - -comptime { - if (builtin.target.cpu.arch != .wasm32) { - @compileError("This platform is for WebAssembly only. You need to pass `--target wasm32` to the Roc compiler."); - } -} - -const Align = extern struct { a: usize, b: usize }; -extern fn malloc(size: usize) callconv(.C) ?*align(@alignOf(Align)) anyopaque; -extern fn realloc(c_ptr: [*]align(@alignOf(Align)) u8, size: usize) callconv(.C) ?*anyopaque; -extern fn free(c_ptr: [*]align(@alignOf(Align)) u8) callconv(.C) void; -extern fn memcpy(dest: *anyopaque, src: *anyopaque, count: usize) *anyopaque; - -export fn roc_alloc(size: usize, alignment: u32) callconv(.C) ?*anyopaque { - _ = alignment; - - return malloc(size); -} - -export fn roc_realloc(c_ptr: *anyopaque, new_size: usize, old_size: usize, alignment: u32) callconv(.C) ?*anyopaque { - _ = old_size; - _ = alignment; - - return realloc(@as([*]align(@alignOf(Align)) u8, @alignCast(@ptrCast(c_ptr))), new_size); -} - -export fn roc_dealloc(c_ptr: *anyopaque, alignment: u32) callconv(.C) void { - _ = alignment; - - free(@as([*]align(@alignOf(Align)) u8, @alignCast(@ptrCast(c_ptr)))); -} - -// NOTE roc_panic and roc_dbg is provided in the JS file, so it can throw an exception - -extern fn roc__main_for_host_1_exposed(*RocStr) void; - -extern fn js_display_roc_string(str_bytes: ?[*]u8, str_len: usize) void; - -pub export fn main() u8 { - // actually call roc to populate the callresult - var callresult = RocStr.empty(); - roc__main_for_host_1_exposed(&callresult); - - // display the result using JavaScript - js_display_roc_string(callresult.asU8ptrMut(), callresult.len()); - - callresult.decref(); - - return 0; -} diff --git a/examples/nodejs-interop/wasm/platform/main.roc b/examples/nodejs-interop/wasm/platform/main.roc deleted file mode 100644 index d8d7292582e..00000000000 --- a/examples/nodejs-interop/wasm/platform/main.roc +++ /dev/null @@ -1,9 +0,0 @@ -platform "wasm-nodejs-example-platform" - requires {} { main : Str } - exposes [] - packages {} - imports [] - provides [main_for_host] - -main_for_host : Str -main_for_host = main diff --git a/examples/python-interop/README.md b/examples/python-interop/README.md deleted file mode 100644 index b58779c94dc..00000000000 --- a/examples/python-interop/README.md +++ /dev/null @@ -1,82 +0,0 @@ -# Python Interop - -This is a demo for calling Roc code from [Python](https://www.python.org/). - -## Installation - -The following was tested on NixOS, with `Python 3.10`, `clang 13.0.1`, `gcc 11.3.0` but should work with most recent versions of those on most modern Linux and MacOS.\ -Of course you're welcome to test on your machine and tell me (via [Zulip](https://roc.zulipchat.com/#narrow/pm-with/583319-dank)) if you ran into any issues or limitations. - -> Because of some rough edges, the linux installation may be a bit more involved (nothing too bad, mostly stuff like renames), so for your convenience I made a small shell script to help out. - -Now in favor of users of all OSs, let's first do a step by step walkthrough on how the build process works, and later, a few key notes on the implementation. - -## Building the Roc library - -First, `cd` into this directory and run this in your terminal: - -```sh -roc build --lib -``` - -This compiles your Roc code into a binary library in the current directory. The library's filename will be `libhello` plus an OS-specific extension (e.g. `libhello.dylib` on macOS). - -## Some Linux Specific Prep Work -As of the time of writing this document, `roc build --lib` generates a shared object with the suffix `.so.1.0`.\ -This `.0` suffix is not needed by neither the application nor Python, so we can simply rename it. - -``` sh -mv libhello.so.1.0 libhello.so.1 -``` -But, one of which does expect plain libhello.so, so we symlink it: - -```sh -ln -sf libhello.so.1 libhello.so -``` - -Also, one thing about dynamically linked applications like this one, is that they need to know where to look for its shared object dependencies, so we need to let CPython know that we hold libhello in this directory, so: - -``` sh -export LD_LIBRARY_PATH=$(pwd):$LD_LIBRARY_PATH -``` - -That wasn't so bad and we're already done with prep work, all that's left it to build our C extension. -## Building the C Extension -``` sh -# If you want, you can set the environment variable cc, to compile with clang instead of gcc -python -m venv .interop_env -source .interop_env/bin/activate # activate.fish if you like fish -python setup.py install -``` -For cleanness sake, we make virtual environment here, but do note you don't have to if you don't want to. -You can also run `python setup.py build` and grab the output shared object from the `build/lib.*` directory.\ -Shared objects are simply importable in CPython (which is great!), so you can just start up an interpreter in the same directory as your new `demo.so` and get the same result. - -**Note -** after all is said and done, for prolonged use, you may want to move your shared library (lib hello) to somewhere permanent on your `LD_LIBRARY_PATH`, or add your desired directory to `LD_LIBRARY_PATH` in some way (e.g put it in your shell .rc). - -## Try it out! - -Now we can see our work by entering an interactive shell and calling our function! - -```py -❯ python -q ->>> import demo ->>> demo.call_roc(21) -'The number was 21, OH YEAH!!! 🤘🤘' -``` - -## Notes on implementation -The structure of python-interop is very similar to a C-Extension, in fact, it is one.\ -We have: -- [`PyModuleDef demoModule`](https://docs.python.org/3/c-api/module.html#c.PyModuleDef) struct which declares the `demo` python module, -- [`PyMethodDef DemoMethods`](https://docs.python.org/3/c-api/structures.html#c.PyMethodDef) struct which declares `demo`'s methods, -- [`PyMODINIT_FUNC PyInit_demo`](https://docs.python.org/3/extending/extending.html) which is `demo`’s initialization function, and of course, -- [`PyObject * call_roc`] which is our demo function! Getting args and returning our string to the interpreter. The Roc-Python bridge lives here, all the above are merely CPython boilerplate to wrap up a C implementation into a valid Python module. - -The first three are basically the backbone of any C-API extension.\ -In addition, a couple more functions and notes you may want to pay attention to: -- [`void roc_panic`] - When creating such interpreter-dependent code, it is reasonable to make the implementation of this function fire up an interpreter Exception (e.g `RuntimeError` or whatever suits). -- When I first came across another implementation, I was a bit confused about `extern void roc__main_for_host_1_exposed_generic`, so let me clarify - this is an external function, implemented by the application, that goes (on the application side-) by the name `main_for_host`. - -And one last thing - -- When writing such the C glue (here, `demo.c`), you need to pay attention to not only Python's reference counting, but also Roc's, so remember to wear seatbelts and decrement your ref counts. diff --git a/examples/python-interop/demo.c b/examples/python-interop/demo.c deleted file mode 100644 index 99ba3902b8b..00000000000 --- a/examples/python-interop/demo.c +++ /dev/null @@ -1,253 +0,0 @@ -#define PY_SSIZE_T_CLEAN -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void *roc_alloc(size_t size, unsigned int alignment) -{ - return malloc(size); -} - -void *roc_realloc(void *ptr, size_t new_size, size_t old_size, - unsigned int alignment) -{ - return realloc(ptr, new_size); -} - -void roc_dealloc(void *ptr, unsigned int alignment) { free(ptr); } - -__attribute__((noreturn)) void roc_panic(void *ptr, unsigned int alignment) -{ - PyErr_SetString(PyExc_RuntimeError, (char *)ptr); -} - -void roc_dbg(char* loc, char* msg, char* src) { - fprintf(stderr, "[%s] %s = %s\n", loc, src, msg); -} - -void *roc_memset(void *str, int c, size_t n) { return memset(str, c, n); } - -// Reference counting - -// If the refcount is set to this, that means the allocation is -// stored in readonly memory in the binary, and we must not -// attempt to increment or decrement it; if we do, we'll segfault! -const ssize_t REFCOUNT_READONLY = 0; -const ssize_t REFCOUNT_ONE = (ssize_t)PTRDIFF_MIN; -const size_t MASK = (size_t)PTRDIFF_MIN; - -// Increment reference count, given a pointer to the first element in a collection. -// We don't need to check for overflow because in order to overflow a usize worth of refcounts, -// you'd need to somehow have more pointers in memory than the OS's virtual address space can hold. -void incref(uint8_t* bytes, uint32_t alignment) -{ - ssize_t *refcount_ptr = ((ssize_t *)bytes) - 1; - ssize_t refcount = *refcount_ptr; - - if (refcount != REFCOUNT_READONLY) { - *refcount_ptr = refcount + 1; - } -} - -// Decrement reference count, given a pointer to the first element in a collection. -// Then call roc_dealloc if nothing is referencing this collection anymore. -void decref(uint8_t* bytes, uint32_t alignment) -{ - if (bytes == NULL) { - return; - } - - size_t extra_bytes = (sizeof(size_t) >= (size_t)alignment) ? sizeof(size_t) : (size_t)alignment; - ssize_t *refcount_ptr = ((ssize_t *)bytes) - 1; - ssize_t refcount = *refcount_ptr; - - if (refcount != REFCOUNT_READONLY) { - *refcount_ptr = refcount - 1; - - if (refcount == REFCOUNT_ONE) { - void *original_allocation = (void *)(refcount_ptr - (extra_bytes - sizeof(size_t))); - - roc_dealloc(original_allocation, alignment); - } - } -} - -// RocBytes (List U8) - -struct RocBytes -{ - uint8_t *bytes; - size_t len; - size_t capacity; -}; - -struct RocBytes init_rocbytes(uint8_t *bytes, size_t len) -{ - if (len == 0) - { - struct RocBytes ret = { - .len = 0, - .bytes = NULL, - .capacity = 0, - }; - - return ret; - } - else - { - struct RocBytes ret; - size_t refcount_size = sizeof(size_t); - uint8_t *new_content = ((uint8_t *)roc_alloc(len + refcount_size, alignof(size_t))) + refcount_size; - - memcpy(new_content, bytes, len); - - ret.bytes = new_content; - ret.len = len; - ret.capacity = len; - - return ret; - } -} - -// RocStr - -struct RocStr -{ - uint8_t *bytes; - size_t len; - size_t capacity; -}; - -struct RocStr init_rocstr(uint8_t *bytes, size_t len) -{ - if (len == 0) - { - struct RocStr ret = { - .len = 0, - .bytes = NULL, - .capacity = MASK, - }; - - return ret; - } - else if (len < sizeof(struct RocStr)) - { - // Start out with zeroed memory, so that - // if we end up comparing two small RocStr values - // for equality, we won't risk memory garbage resulting - // in two equal strings appearing unequal. - struct RocStr ret = { - .len = 0, - .bytes = NULL, - .capacity = MASK, - }; - - // Copy the bytes into the stack allocation - memcpy(&ret, bytes, len); - - // Record the string's length in the last byte of the stack allocation - ((uint8_t *)&ret)[sizeof(struct RocStr) - 1] = (uint8_t)len | 0b10000000; - - return ret; - } - else - { - // A large RocStr is the same as a List U8 (aka RocBytes) in memory. - struct RocBytes roc_bytes = init_rocbytes(bytes, len); - - struct RocStr ret = { - .len = roc_bytes.len, - .bytes = roc_bytes.bytes, - .capacity = roc_bytes.capacity, - }; - - return ret; - } -} - -bool is_small_str(struct RocStr str) { return ((ssize_t)str.capacity) < 0; } - -// Determine the length of the string, taking into -// account the small string optimization -size_t roc_str_len(struct RocStr str) -{ - uint8_t *bytes = (uint8_t *)&str; - uint8_t last_byte = bytes[sizeof(str) - 1]; - uint8_t last_byte_xored = last_byte ^ 0b10000000; - size_t small_len = (size_t)(last_byte_xored); - size_t big_len = str.len; - - // Avoid branch misprediction costs by always - // determining both small_len and big_len, - // so this compiles to a cmov instruction. - if (is_small_str(str)) - { - return small_len; - } - else - { - return big_len; - } -} - -extern void roc__main_for_host_1_exposed_generic(struct RocBytes *ret, struct RocBytes *arg); - -// Receive a value from Python, JSON serialized it and pass it to Roc as a List U8 -// (at which point the Roc platform will decode it and crash if it's invalid, -// which roc_panic will translate into a Python exception), then get some JSON back from Roc -// - also as a List U8 - and have Python JSON.parse it into a plain Python value to return. -PyObject * call_roc(PyObject *self, PyObject *args) -{ - int num; - - if(!PyArg_ParseTuple(args, "i", &num)) { - return NULL; - } - - char str_num[256] = {0}; - sprintf(str_num, "%d", num); - - // can also be done with python objects but im not sure what would be the benefit here - // PyObject* py_num_str = PyUnicode_FromFormat("%d", num); - // const char* c_str = PyUnicode_AsUTF8(py_num_str); - // size_t length = (size_t *)PyUnicode_GetLength(py_num_str); - // ...init_rocbytes((uint8_t *)c_str, length); - - // Turn the given Python number into a JSON string. - struct RocBytes arg = init_rocbytes((uint8_t *)str_num, strlen(str_num)); - struct RocBytes ret; - - // Call the Roc function to populate `ret`'s bytes. - roc__main_for_host_1_exposed_generic(&ret, &arg); - - // Create a Python string from the heap-allocated JSON bytes the Roc function returned. - PyObject* py_obj = PyUnicode_FromStringAndSize((char*)ret.bytes, ret.len); - - // Now that we've created py_str, we're no longer referencing the RocBytes. - decref((void *)&ret, alignof(uint8_t *)); - - return py_obj; -} - -static PyMethodDef DemoMethods[] = { - {"call_roc", call_roc, METH_VARARGS, "Calls a Roc function with a number, returns a string interpolated with the number"}, - {NULL, NULL, 0, NULL} -}; - -static struct PyModuleDef demoModule = { - PyModuleDef_HEAD_INIT, - "call_roc", - "demo roc call", - -1, - DemoMethods -}; - -PyMODINIT_FUNC PyInit_demo(void) { - return PyModule_Create(&demoModule); -} diff --git a/examples/python-interop/libhello.roc b/examples/python-interop/libhello.roc deleted file mode 100644 index 1ee460ee2a6..00000000000 --- a/examples/python-interop/libhello.roc +++ /dev/null @@ -1,10 +0,0 @@ -app [main] { pf: platform "platform/main.roc" } - -main : U64 -> Str -main = \num -> - if num == 0 then - "I need a positive number here!" - else - str = Num.to_str(num) - - "The number was $(str), OH YEAH!!! 🤘🤘" diff --git a/examples/python-interop/platform/host.c b/examples/python-interop/platform/host.c deleted file mode 100644 index 393790bf4d5..00000000000 --- a/examples/python-interop/platform/host.c +++ /dev/null @@ -1,115 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include // shm_open -#include // for mmap -#include // for kill - -void* roc_alloc(size_t size, unsigned int alignment) { return malloc(size); } - -void* roc_realloc(void* ptr, size_t new_size, size_t old_size, - unsigned int alignment) { - return realloc(ptr, new_size); -} - -void roc_dealloc(void* ptr, unsigned int alignment) { free(ptr); } - -void roc_panic(void* ptr, unsigned int alignment) { - char* msg = (char*)ptr; - fprintf(stderr, - "Application crashed with message\n\n %s\n\nShutting down\n", msg); - exit(1); -} - -void roc_dbg(char* loc, char* msg, char* src) { - fprintf(stderr, "[%s] %s = %s\n", loc, src, msg); -} - -void* roc_memset(void* str, int c, size_t n) { return memset(str, c, n); } - -int roc_shm_open(char* name, int oflag, int mode) { -#ifdef _WIN32 - return 0; -#else - return shm_open(name, oflag, mode); -#endif -} -void* roc_mmap(void* addr, int length, int prot, int flags, int fd, int offset) { -#ifdef _WIN32 - return addr; -#else - return mmap(addr, length, prot, flags, fd, offset); -#endif -} - -int roc_getppid() { -#ifdef _WIN32 - return 0; -#else - return getppid(); -#endif -} - -struct RocStr { - char* bytes; - size_t len; - size_t capacity; -}; - -bool is_small_str(struct RocStr str) { return ((ssize_t)str.capacity) < 0; } - -// Determine the length of the string, taking into -// account the small string optimization -size_t roc_str_len(struct RocStr str) { - char* bytes = (char*)&str; - char last_byte = bytes[sizeof(str) - 1]; - char last_byte_xored = last_byte ^ 0b10000000; - size_t small_len = (size_t)(last_byte_xored); - size_t big_len = str.len; - - // Avoid branch misprediction costs by always - // determining both small_len and big_len, - // so this compiles to a cmov instruction. - if (is_small_str(str)) { - return small_len; - } else { - return big_len; - } -} - -extern void roc__main_for_host_1_exposed_generic(struct RocStr *string); - -int main() { - - struct RocStr str; - roc__main_for_host_1_exposed_generic(&str); - - // Determine str_len and the str_bytes pointer, - // taking into account the small string optimization. - size_t str_len = roc_str_len(str); - char* str_bytes; - - if (is_small_str(str)) { - str_bytes = (char*)&str; - } else { - str_bytes = str.bytes; - } - - // Write to stdout - if (write(1, str_bytes, str_len) >= 0) { - // Writing succeeded! - - // NOTE: the string is a static string, read from in the binary - // if you make it a heap-allocated string, it'll be leaked here - return 0; - } else { - printf("Error writing to stdout: %s\n", strerror(errno)); - - // NOTE: the string is a static string, read from in the binary - // if you make it a heap-allocated string, it'll be leaked here - return 1; - } -} diff --git a/examples/python-interop/platform/main.roc b/examples/python-interop/platform/main.roc deleted file mode 100644 index e81dd52fb7a..00000000000 --- a/examples/python-interop/platform/main.roc +++ /dev/null @@ -1,16 +0,0 @@ -platform "python-interop" - requires {} { main : U64 -> Str } - exposes [] - packages {} - imports [] - provides [main_for_host] - -main_for_host : List U8 -> List U8 -main_for_host = \input -> - when Str.from_utf8(input) is - Ok(arg) -> - when Str.to_u64(arg) is - Ok(num) -> main(num) |> Str.to_utf8 - Err(_) -> [] - - Err(_) -> [] diff --git a/examples/ruby-interop/demo.c b/examples/ruby-interop/demo.c deleted file mode 100644 index f3a73f30fd8..00000000000 --- a/examples/ruby-interop/demo.c +++ /dev/null @@ -1,225 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include "extconf.h" - -void *roc_alloc(size_t size, unsigned int alignment) { return malloc(size); } - -void *roc_realloc(void *ptr, size_t new_size, size_t old_size, - unsigned int alignment) -{ - return realloc(ptr, new_size); -} - -void roc_dealloc(void *ptr, unsigned int alignment) { free(ptr); } - -__attribute__((noreturn)) void roc_panic(void *ptr, unsigned int alignment) -{ - rb_raise(rb_eException, "%s", (char *)ptr); -} - -void roc_dbg(char* loc, char* msg, char* src) { - fprintf(stderr, "[%s] %s = %s\n", loc, src, msg); -} - -void *roc_memset(void *str, int c, size_t n) { return memset(str, c, n); } - -// Reference counting - -// If the refcount is set to this, that means the allocation is -// stored in readonly memory in the binary, and we must not -// attempt to increment or decrement it; if we do, we'll segfault! -const ssize_t REFCOUNT_READONLY = 0; -const ssize_t REFCOUNT_ONE = (ssize_t)PTRDIFF_MIN; -const size_t MASK = (size_t)PTRDIFF_MIN; - -// Increment reference count, given a pointer to the first element in a collection. -// We don't need to check for overflow because in order to overflow a usize worth of refcounts, -// you'd need to somehow have more pointers in memory than the OS's virtual address space can hold. -void incref(uint8_t* bytes, uint32_t alignment) -{ - ssize_t *refcount_ptr = ((ssize_t *)bytes) - 1; - ssize_t refcount = *refcount_ptr; - - if (refcount != REFCOUNT_READONLY) { - *refcount_ptr = refcount + 1; - } -} - -// Decrement reference count, given a pointer to the first element in a collection. -// Then call roc_dealloc if nothing is referencing this collection anymore. -void decref(uint8_t* bytes, uint32_t alignment) -{ - if (bytes == NULL) { - return; - } - - size_t extra_bytes = (sizeof(size_t) >= (size_t)alignment) ? sizeof(size_t) : (size_t)alignment; - ssize_t *refcount_ptr = ((ssize_t *)bytes) - 1; - ssize_t refcount = *refcount_ptr; - - if (refcount != REFCOUNT_READONLY) { - *refcount_ptr = refcount - 1; - - if (refcount == REFCOUNT_ONE) { - void *original_allocation = (void *)(refcount_ptr - (extra_bytes - sizeof(size_t))); - - roc_dealloc(original_allocation, alignment); - } - } -} - -// RocBytes (List U8) - -struct RocBytes -{ - uint8_t *bytes; - size_t len; - size_t capacity; -}; - -struct RocBytes init_rocbytes(uint8_t *bytes, size_t len) -{ - if (len == 0) - { - struct RocBytes ret = { - .len = 0, - .bytes = NULL, - .capacity = 0, - }; - - return ret; - } - else - { - struct RocBytes ret; - size_t refcount_size = sizeof(size_t); - uint8_t *new_content = (uint8_t *)roc_alloc(len + refcount_size, alignof(size_t)) + refcount_size; - - memcpy(new_content, bytes, len); - - ret.bytes = new_content; - ret.len = len; - ret.capacity = len; - - return ret; - } -} - -// RocStr - -struct RocStr -{ - uint8_t *bytes; - size_t len; - size_t capacity; -}; - -struct RocStr init_rocstr(uint8_t *bytes, size_t len) -{ - if (len == 0) - { - struct RocStr ret = { - .len = 0, - .bytes = NULL, - .capacity = MASK, - }; - - return ret; - } - else if (len < sizeof(struct RocStr)) - { - // Start out with zeroed memory, so that - // if we end up comparing two small RocStr values - // for equality, we won't risk memory garbage resulting - // in two equal strings appearing unequal. - struct RocStr ret = { - .len = 0, - .bytes = NULL, - .capacity = MASK, - }; - - // Copy the bytes into the stack allocation - memcpy(&ret, bytes, len); - - // Record the string's length in the last byte of the stack allocation - ((uint8_t *)&ret)[sizeof(struct RocStr) - 1] = (uint8_t)len | 0b10000000; - - return ret; - } - else - { - // A large RocStr is the same as a List U8 (aka RocBytes) in memory. - struct RocBytes roc_bytes = init_rocbytes(bytes, len); - - struct RocStr ret = { - .len = roc_bytes.len, - .bytes = roc_bytes.bytes, - .capacity = roc_bytes.capacity, - }; - - return ret; - } -} - -bool is_small_str(struct RocStr str) { return ((ssize_t)str.capacity) < 0; } - -// Determine the length of the string, taking into -// account the small string optimization -size_t roc_str_len(struct RocStr str) -{ - uint8_t *bytes = (uint8_t *)&str; - uint8_t last_byte = bytes[sizeof(str) - 1]; - uint8_t last_byte_xored = last_byte ^ 0b10000000; - size_t small_len = (size_t)(last_byte_xored); - size_t big_len = str.len; - - // Avoid branch misprediction costs by always - // determining both small_len and big_len, - // so this compiles to a cmov instruction. - if (is_small_str(str)) - { - return small_len; - } - else - { - return big_len; - } -} - -extern void roc__main_for_host_1_exposed_generic(struct RocBytes *ret, struct RocBytes *arg); - -// Receive a value from Ruby, serialize it and pass it to Roc as a List U8 -// (at which point the Roc platform will decode it and crash if it's invalid, -// which roc_panic will translate into a Ruby exception), then get some utf-8 string back from Roc -// - also as a List U8 - and have Ruby decode it into a plain Ruby value to return. -VALUE call_roc(VALUE self, VALUE rb_arg) -{ - VALUE str_arg = rb_funcall(rb_arg, rb_intern("to_s"), 0); - VALUE str_utf8_arg = rb_funcall(str_arg, rb_intern("force_encoding"), 1, rb_str_new_cstr("utf-8")); - - struct RocBytes arg = init_rocbytes((uint8_t *)RSTRING_PTR(str_utf8_arg), RSTRING_LEN(str_utf8_arg)); - struct RocBytes ret; - - // Call the Roc function to populate `ret`'s bytes. - roc__main_for_host_1_exposed_generic(&ret, &arg); - - // Create a rb_utf8_str from the heap-allocated utf-8 bytes the Roc function returned. - VALUE returned_str = rb_utf8_str_new((char *)ret.bytes, ret.len); - - // Now that we've created our Ruby string, we're no longer referencing the RocBytes. - decref((void *)&ret, alignof(uint8_t *)); - - return returned_str; -} - -void Init_demo() -{ - VALUE roc_app = rb_define_module("RocApp"); - rb_define_module_function(roc_app, "call_roc", &call_roc, 1); -} diff --git a/examples/ruby-interop/libhello.roc b/examples/ruby-interop/libhello.roc deleted file mode 100644 index 1ee460ee2a6..00000000000 --- a/examples/ruby-interop/libhello.roc +++ /dev/null @@ -1,10 +0,0 @@ -app [main] { pf: platform "platform/main.roc" } - -main : U64 -> Str -main = \num -> - if num == 0 then - "I need a positive number here!" - else - str = Num.to_str(num) - - "The number was $(str), OH YEAH!!! 🤘🤘" diff --git a/examples/ruby-interop/platform/host.c b/examples/ruby-interop/platform/host.c deleted file mode 100644 index f27aaefd991..00000000000 --- a/examples/ruby-interop/platform/host.c +++ /dev/null @@ -1,119 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include // shm_open -#include // for mmap -#include // for kill - -void* roc_alloc(size_t size, unsigned int alignment) { return malloc(size); } - -void* roc_realloc(void* ptr, size_t new_size, size_t old_size, - unsigned int alignment) { - return realloc(ptr, new_size); -} - -void roc_dealloc(void* ptr, unsigned int alignment) { free(ptr); } - -void roc_panic(void* ptr, unsigned int alignment) { - char* msg = (char*)ptr; - fprintf(stderr, - "Application crashed with message\n\n %s\n\nShutting down\n", msg); - exit(1); -} - -void roc_dbg(char* loc, char* msg, char* src) { - fprintf(stderr, "[%s] %s = %s\n", loc, src, msg); -} - -void* roc_memmove(void* dest, const void* src, size_t n){ - return memmove(dest, src, n); -} - -void* roc_memset(void* str, int c, size_t n) { return memset(str, c, n); } - -int roc_shm_open(char* name, int oflag, int mode) { -#ifdef _WIN32 - return 0; -#else - return shm_open(name, oflag, mode); -#endif -} -void* roc_mmap(void* addr, int length, int prot, int flags, int fd, int offset) { -#ifdef _WIN32 - return addr; -#else - return mmap(addr, length, prot, flags, fd, offset); -#endif -} - -int roc_getppid() { -#ifdef _WIN32 - return 0; -#else - return getppid(); -#endif -} - -struct RocStr { - char* bytes; - size_t len; - size_t capacity; -}; - -bool is_small_str(struct RocStr str) { return ((ssize_t)str.capacity) < 0; } - -// Determine the length of the string, taking into -// account the small string optimization -size_t roc_str_len(struct RocStr str) { - char* bytes = (char*)&str; - char last_byte = bytes[sizeof(str) - 1]; - char last_byte_xored = last_byte ^ 0b10000000; - size_t small_len = (size_t)(last_byte_xored); - size_t big_len = str.len; - - // Avoid branch misprediction costs by always - // determining both small_len and big_len, - // so this compiles to a cmov instruction. - if (is_small_str(str)) { - return small_len; - } else { - return big_len; - } -} - -extern void roc__main_for_host_1_exposed_generic(struct RocStr *string); - -int main() { - - struct RocStr str; - roc__main_for_host_1_exposed_generic(&str); - - // Determine str_len and the str_bytes pointer, - // taking into account the small string optimization. - size_t str_len = roc_str_len(str); - char* str_bytes; - - if (is_small_str(str)) { - str_bytes = (char*)&str; - } else { - str_bytes = str.bytes; - } - - // Write to stdout - if (write(1, str_bytes, str_len) >= 0) { - // Writing succeeded! - - // NOTE: the string is a static string, read from in the binary - // if you make it a heap-allocated string, it'll be leaked here - return 0; - } else { - printf("Error writing to stdout: %s\n", strerror(errno)); - - // NOTE: the string is a static string, read from in the binary - // if you make it a heap-allocated string, it'll be leaked here - return 1; - } -} diff --git a/examples/ruby-interop/platform/main.roc b/examples/ruby-interop/platform/main.roc deleted file mode 100644 index 33ad40d532f..00000000000 --- a/examples/ruby-interop/platform/main.roc +++ /dev/null @@ -1,16 +0,0 @@ -platform "ruby-interop" - requires {} { main : U64 -> Str } - exposes [] - packages {} - imports [] - provides [main_for_host] - -main_for_host : List U8 -> List U8 -main_for_host = \input -> - when Str.from_utf8(input) is - Ok(arg) -> - when Str.to_u64(arg) is - Ok(num) -> main(num) |> Str.to_utf8 - Err(_) -> [] - - Err(_) -> [] # TODO panic so that Ruby raises an exception diff --git a/examples/virtual-dom-wip/ExampleApp.roc b/examples/virtual-dom-wip/ExampleApp.roc deleted file mode 100644 index 9b08414bdbc..00000000000 --- a/examples/virtual-dom-wip/ExampleApp.roc +++ /dev/null @@ -1,36 +0,0 @@ -module [example_app, State] - -import pf.Html exposing [App, Html, html, head, body, div, text, h1] - -State : { - answer : U32, -} - -example_app : App State State -example_app = { - init, - render, - wasm_url: "assets/example-client.wasm", -} - -init = \result -> - when result is - Ok(state) -> state - Err(_) -> { answer: 0 } - -render : State -> Html State -render = \state -> - num = Num.to_str(state.answer) - - html([], [ - head([], []), - body([], [ - h1([], [text("The app")]), - div([], [text("The answer is $(num)")]), - ]), - ]) - -expect - Html.render_static(Html.translate_static(render({ answer: 42 }))) - == - "

The app

The answer is 42
" diff --git a/examples/virtual-dom-wip/example-client.roc b/examples/virtual-dom-wip/example-client.roc deleted file mode 100644 index 9799c2185e8..00000000000 --- a/examples/virtual-dom-wip/example-client.roc +++ /dev/null @@ -1,5 +0,0 @@ -app [app] { pf: platform "platform/client-side.roc" } - -import ExampleApp exposing [example_app] - -app = example_app diff --git a/examples/virtual-dom-wip/example-server.roc b/examples/virtual-dom-wip/example-server.roc deleted file mode 100644 index e2c3a14c1a1..00000000000 --- a/examples/virtual-dom-wip/example-server.roc +++ /dev/null @@ -1,5 +0,0 @@ -app [app] { pf: platform "platform/server-side.roc" } - -import ExampleApp exposing [example_app] - -app = example_app diff --git a/examples/virtual-dom-wip/platform/Action.roc b/examples/virtual-dom-wip/platform/Action.roc deleted file mode 100644 index 87d03160b54..00000000000 --- a/examples/virtual-dom-wip/platform/Action.roc +++ /dev/null @@ -1,15 +0,0 @@ -module [Action, none, update, map] - -Action state : [None, Update state] - -none : Action * -none = None - -update : state -> Action state -update = Update - -map : Action a, (a -> b) -> Action b -map = \action, transform -> - when action is - None -> None - Update(state) -> Update(transform(state)) diff --git a/examples/virtual-dom-wip/platform/Html.roc b/examples/virtual-dom-wip/platform/Html.roc deleted file mode 100644 index 6a11dc1a3a8..00000000000 --- a/examples/virtual-dom-wip/platform/Html.roc +++ /dev/null @@ -1,271 +0,0 @@ -module [ - App, - Html, - Attribute, - render_static, - render_static_without_doc_type, - translate, - translate_static, - text, - none, - html, - base, - head, - link, - meta, - style, - title, - body, - address, - article, - aside, - footer, - header, - h1, - h2, - h3, - h4, - h5, - h6, - main, - nav, - section, - blockquote, - dd, - div, - dl, - dt, - figcaption, - figure, - hr, - li, - menu, - ol, - p, - pre, - ul, - a, - abbr, - b, - bdi, - bdo, - br, - cite, - code, - data, - dfn, - em, - i, - kbd, - mark, - q, - rp, - rt, - ruby, - s, - samp, - small, - span, - strong, - sub, - sup, - time, - u, - var, - wbr, - area, - audio, - img, - map, - track, - video, - embed, - iframe, - object, - picture, - portal, - source, - svg, - math, - canvas, - noscript, - script, - del, - ins, - caption, - col, - colgroup, - table, - tbody, - td, - tfoot, - th, - thead, - tr, - button, - datalist, - fieldset, - form, - input, - label, - legend, - meter, - optgroup, - option, - output, - progress, - select, - textarea, - details, - dialog, - summary, - slot, - template, -] - -import Html.Internal.Shared -import Html.Internal.Server - -App state init_data : Html.Internal.Shared.App state init_data -Html state : Html.Internal.Shared.Html state -Attribute state : Html.Internal.Shared.Attribute state - -element = Html.Internal.Shared.element -text = Html.Internal.Shared.text -none = Html.Internal.Shared.none - -translate = Html.Internal.Shared.translate -translate_static = Html.Internal.Shared.translate_static - -## Render a static Html node to a string, for saving to disk or sending over a network -## -## The output has no whitespace between nodes, to make it small. -## This is intended for generating full HTML documents, so it -## automatically adds `` to the start of the string. -## See also `renderStaticWithoutDocType`. -render_static : Html [] -> Str -render_static = \node -> - buffer = Str.reserve("", Html.Internal.Shared.node_size(node)) - - Html.Internal.Server.append_rendered_static(buffer, node) - -## Render a Html node to a static string, without a DOCTYPE -render_static_without_doc_type : Html [] -> Str -render_static_without_doc_type = \node -> - buffer = Str.reserve("", Html.Internal.Shared.node_size(node)) - - Html.Internal.Server.append_rendered_static(buffer, node) - -html = element("html") -base = element("base") -head = element("head") -link = element("link") -meta = element("meta") -style = element("style") -title = element("title") -body = element("body") -address = element("address") -article = element("article") -aside = element("aside") -footer = element("footer") -header = element("header") -h1 = element("h1") -h2 = element("h2") -h3 = element("h3") -h4 = element("h4") -h5 = element("h5") -h6 = element("h6") -main = element("main") -nav = element("nav") -section = element("section") -blockquote = element("blockquote") -dd = element("dd") -div = element("div") -dl = element("dl") -dt = element("dt") -figcaption = element("figcaption") -figure = element("figure") -hr = element("hr") -li = element("li") -menu = element("menu") -ol = element("ol") -p = element("p") -pre = element("pre") -ul = element("ul") -a = element("a") -abbr = element("abbr") -b = element("b") -bdi = element("bdi") -bdo = element("bdo") -br = element("br") -cite = element("cite") -code = element("code") -data = element("data") -dfn = element("dfn") -em = element("em") -i = element("i") -kbd = element("kbd") -mark = element("mark") -q = element("q") -rp = element("rp") -rt = element("rt") -ruby = element("ruby") -s = element("s") -samp = element("samp") -small = element("small") -span = element("span") -strong = element("strong") -sub = element("sub") -sup = element("sup") -time = element("time") -u = element("u") -var = element("var") -wbr = element("wbr") -area = element("area") -audio = element("audio") -img = element("img") -map = element("map") -track = element("track") -video = element("video") -embed = element("embed") -iframe = element("iframe") -object = element("object") -picture = element("picture") -portal = element("portal") -source = element("source") -svg = element("svg") -math = element("math") -canvas = element("canvas") -noscript = element("noscript") -script = element("script") -del = element("del") -ins = element("ins") -caption = element("caption") -col = element("col") -colgroup = element("colgroup") -table = element("table") -tbody = element("tbody") -td = element("td") -tfoot = element("tfoot") -th = element("th") -thead = element("thead") -tr = element("tr") -button = element("button") -datalist = element("datalist") -fieldset = element("fieldset") -form = element("form") -input = element("input") -label = element("label") -legend = element("legend") -meter = element("meter") -optgroup = element("optgroup") -option = element("option") -output = element("output") -progress = element("progress") -select = element("select") -textarea = element("textarea") -details = element("details") -dialog = element("dialog") -summary = element("summary") -slot = element("slot") -template = element("template") diff --git a/examples/virtual-dom-wip/platform/Html/Attributes.roc b/examples/virtual-dom-wip/platform/Html/Attributes.roc deleted file mode 100644 index 8dd510ef035..00000000000 --- a/examples/virtual-dom-wip/platform/Html/Attributes.roc +++ /dev/null @@ -1,273 +0,0 @@ -module [ - attribute, - accept, - accept_charset, - accesskey, - action, - align, - allow, - alt, - async, - autocapitalize, - autocomplete, - autofocus, - autoplay, - background, - bgcolor, - border, - buffered, - capture, - challenge, - charset, - checked, - cite, - class, - code, - codebase, - color, - cols, - colspan, - content, - contenteditable, - contextmenu, - controls, - coords, - crossorigin, - csp, - data, - # dataAttr, TODO - datetime, - decoding, - default, - defer, - dir, - dirname, - disabled, - download, - draggable, - enctype, - enterkeyhint, - for, - form, - formaction, - formenctype, - formmethod, - formnovalidate, - formtarget, - headers, - height, - hidden, - high, - href, - hreflang, - http_equiv, - icon, - id, - importance, - integrity, - intrinsicsize, - inputmode, - ismap, - itemprop, - keytype, - kind, - label, - lang, - language, - loading, - list, - loop, - low, - manifest, - max, - maxlength, - minlength, - media, - method, - min, - multiple, - muted, - name, - novalidate, - open, - optimum, - pattern, - ping, - placeholder, - poster, - preload, - radiogroup, - readonly, - referrerpolicy, - rel, - required, - reversed, - role, - rows, - rowspan, - sandbox, - scope, - scoped, - selected, - shape, - size, - sizes, - slot, - span, - spellcheck, - src, - srcdoc, - srclang, - srcset, - start, - step, - style, - summary, - tabindex, - target, - title, - translate, - type, - usemap, - value, - width, - wrap, -] - -import Html.Internal.Shared exposing [Attribute] - -attribute : Str -> (Str -> Attribute state) -attribute = \attr_type -> - \attr_value -> HtmlAttr(attr_type, attr_value) - -accept = attribute("accept") -accept_charset = attribute("acceptCharset") -accesskey = attribute("accesskey") -action = attribute("action") -align = attribute("align") -allow = attribute("allow") -alt = attribute("alt") -async = attribute("async") -autocapitalize = attribute("autocapitalize") -autocomplete = attribute("autocomplete") -autofocus = attribute("autofocus") -autoplay = attribute("autoplay") -background = attribute("background") -bgcolor = attribute("bgcolor") -border = attribute("border") -buffered = attribute("buffered") -capture = attribute("capture") -challenge = attribute("challenge") -charset = attribute("charset") -checked = attribute("checked") -cite = attribute("cite") -class = attribute("class") -code = attribute("code") -codebase = attribute("codebase") -color = attribute("color") -cols = attribute("cols") -colspan = attribute("colspan") -content = attribute("content") -contenteditable = attribute("contenteditable") -contextmenu = attribute("contextmenu") -controls = attribute("controls") -coords = attribute("coords") -crossorigin = attribute("crossorigin") -csp = attribute("csp") -data = attribute("data") -datetime = attribute("datetime") -decoding = attribute("decoding") -default = attribute("default") -defer = attribute("defer") -dir = attribute("dir") -dirname = attribute("dirname") -disabled = attribute("disabled") -download = attribute("download") -draggable = attribute("draggable") -enctype = attribute("enctype") -enterkeyhint = attribute("enterkeyhint") -for = attribute("for") -form = attribute("form") -formaction = attribute("formaction") -formenctype = attribute("formenctype") -formmethod = attribute("formmethod") -formnovalidate = attribute("formnovalidate") -formtarget = attribute("formtarget") -headers = attribute("headers") -height = attribute("height") -hidden = attribute("hidden") -high = attribute("high") -href = attribute("href") -hreflang = attribute("hreflang") -http_equiv = attribute("httpEquiv") -icon = attribute("icon") -id = attribute("id") -importance = attribute("importance") -integrity = attribute("integrity") -intrinsicsize = attribute("intrinsicsize") -inputmode = attribute("inputmode") -ismap = attribute("ismap") -itemprop = attribute("itemprop") -keytype = attribute("keytype") -kind = attribute("kind") -label = attribute("label") -lang = attribute("lang") -language = attribute("language") -loading = attribute("loading") -list = attribute("list") -loop = attribute("loop") -low = attribute("low") -manifest = attribute("manifest") -max = attribute("max") -maxlength = attribute("maxlength") -minlength = attribute("minlength") -media = attribute("media") -method = attribute("method") -min = attribute("min") -multiple = attribute("multiple") -muted = attribute("muted") -name = attribute("name") -novalidate = attribute("novalidate") -open = attribute("open") -optimum = attribute("optimum") -pattern = attribute("pattern") -ping = attribute("ping") -placeholder = attribute("placeholder") -poster = attribute("poster") -preload = attribute("preload") -radiogroup = attribute("radiogroup") -readonly = attribute("readonly") -referrerpolicy = attribute("referrerpolicy") -rel = attribute("rel") -required = attribute("required") -reversed = attribute("reversed") -role = attribute("role") -rows = attribute("rows") -rowspan = attribute("rowspan") -sandbox = attribute("sandbox") -scope = attribute("scope") -scoped = attribute("scoped") -selected = attribute("selected") -shape = attribute("shape") -size = attribute("size") -sizes = attribute("sizes") -slot = attribute("slot") -span = attribute("span") -spellcheck = attribute("spellcheck") -src = attribute("src") -srcdoc = attribute("srcdoc") -srclang = attribute("srclang") -srcset = attribute("srcset") -start = attribute("start") -step = attribute("step") -style = attribute("style") -summary = attribute("summary") -tabindex = attribute("tabindex") -target = attribute("target") -title = attribute("title") -translate = attribute("translate") -type = attribute("type") -usemap = attribute("usemap") -value = attribute("value") -width = attribute("width") -wrap = attribute("wrap") diff --git a/examples/virtual-dom-wip/platform/Html/Event.roc b/examples/virtual-dom-wip/platform/Html/Event.roc deleted file mode 100644 index a16c7afabb8..00000000000 --- a/examples/virtual-dom-wip/platform/Html/Event.roc +++ /dev/null @@ -1,77 +0,0 @@ -module [ - Handler, - CyclicStructureAccessor, - on, - custom, - on_click, - on_double_click, - on_mouse_down, - on_mouse_up, - on_mouse_enter, - on_mouse_leave, - on_mouse_over, - on_mouse_out, - on_check, - on_blur, - on_focus, - on_input, - on_submit, -] - -import Action exposing [Action] -import Html.Internal.Shared exposing [Attribute] - -Handler state : Html.Internal.Shared.Handler state -CyclicStructureAccessor : Html.Internal.Shared.CyclicStructureAccessor - -custom : Str, List CyclicStructureAccessor, (state, List (List U8) -> { action : Action state, stop_propagation : Bool, prevent_default : Bool }) -> Attribute state -custom = \event_name, accessors, callback -> - EventListener(event_name, accessors, Custom(callback)) - -on : Str, List CyclicStructureAccessor, (state, List (List U8) -> Action state) -> Attribute state -on = \event_name, accessors, callback -> - EventListener(event_name, accessors, Normal(callback)) - -# Internal helper -curried_on : Str -> (List CyclicStructureAccessor, (state, List (List U8) -> Action state) -> Attribute state) -curried_on = \event_name -> - \accessors, callback -> - EventListener(event_name, accessors, Normal(callback)) - -on_click = curried_on("click") -on_double_click = curried_on("dblclick") -on_mouse_down = curried_on("mousedown") -on_mouse_up = curried_on("mouseup") -on_mouse_enter = curried_on("mouseenter") -on_mouse_leave = curried_on("mouseleave") -on_mouse_over = curried_on("mouseover") -on_mouse_out = curried_on("mouseout") -on_check = curried_on("check") -on_blur = curried_on("blur") -on_focus = curried_on("focus") - -on_input : List CyclicStructureAccessor, (state, List (List U8) -> Action state) -> Attribute state -on_input = \accessors, callback -> - custom_callback : state, List (List U8) -> { action : Action state, stop_propagation : Bool, prevent_default : Bool } - custom_callback = \state, jsons -> { - action: callback(state, jsons), - stop_propagation: Bool.true, - prevent_default: Bool.false, - } - - EventListener("input", accessors, Custom(custom_callback)) - -on_submit : List CyclicStructureAccessor, (state, List (List U8) -> Action state) -> Attribute state -on_submit = \accessors, callback -> - custom_callback = \state, jsons -> { - action: callback(state, jsons), - stop_propagation: Bool.false, - prevent_default: Bool.true, - } - - EventListener("submit", accessors, Custom(custom_callback)) - -# Notes from Elm: -# - stopPropagation causes immediate view update, without waiting for animationFrame, -# to prevent input state getting out of sync with model state when typing fast. -# - Security-sensitive events trigger an immediate update within the same user-instigated tick diff --git a/examples/virtual-dom-wip/platform/Html/Internal/Client.roc b/examples/virtual-dom-wip/platform/Html/Internal/Client.roc deleted file mode 100644 index 24edce4f79d..00000000000 --- a/examples/virtual-dom-wip/platform/Html/Internal/Client.roc +++ /dev/null @@ -1,801 +0,0 @@ -module [ - PlatformState, - init_client_app, - dispatch_event, -] - -import PlatformTasks exposing [ - NodeId, - HandlerId, - TagName, - AttrType, - EventType, -] -import Html.Internal.Shared exposing [ - App, - Html, - Attribute, - CyclicStructureAccessor, - Handler, - translate_static, -] -import Json -import Action - -PlatformState state init_data : { - app : App state init_data, - state, - rendered : RenderedTree state, -} - -# The rendered tree uses indices rather than pointers -# This makes it easier to communicate with JS using integer indices. -# There is a JavaScript `nodes` array that matches the Roc `nodes` List -RenderedTree state : { - root : NodeId, - nodes : List (Result RenderedNode [DeletedNode]), - deleted_node_cache : List NodeId, - handlers : List (Result (Handler state) [DeletedHandler]), - deleted_handler_cache : List HandlerId, -} - -RenderedNode : [ - RenderedNone, - RenderedText Str, - RenderedElement Str RenderedAttributes (List NodeId), -] - -RenderedAttributes : { - event_listeners : Dict Str { accessors : List CyclicStructureAccessor, handler_id : HandlerId }, - html_attrs : Dict Str Str, - dom_props : Dict Str (List U8), - styles : Dict Str Str, -} - -empty_rendered_attrs = { - event_listeners: Dict.empty({}), - html_attrs: Dict.empty({}), - dom_props: Dict.empty({}), - styles: Dict.empty({}), -} - -Patch : [ - CreateElement NodeId TagName, - CreateTextNode NodeId Str, - UpdateTextNode NodeId Str, - AppendChild NodeId NodeId, - RemoveNode NodeId, - ReplaceNode NodeId NodeId, - SetAttribute NodeId AttrType Str, - RemoveAttribute NodeId AttrType, - SetProperty NodeId Str (List U8), - RemoveProperty NodeId Str, - SetStyle NodeId Str Str, - SetListener NodeId EventType (List U8) HandlerId, - RemoveListener NodeId HandlerId, -] - -DiffState state : { rendered : RenderedTree state, patches : List Patch } - -# ------------------------------- -# INITIALISATION -# ------------------------------- -init_client_app : List U8, App state init_data -> Task (PlatformState state init_data) * where init_data implements Decoding -init_client_app = \json, app -> - # Initialise the Roc representation of the rendered DOM, and calculate patches (for event listeners) - { state, rendered, patches } = - init_client_app_help(json, app) - - # Call out to JS to patch the DOM, attaching the event listeners - apply_patches!(patches) - - Task.ok({ - app, - state, - rendered, - }) - -# Testable helper function to initialise the app -init_client_app_help : List U8, App state init_data -> { state, rendered : RenderedTree state, patches : List Patch } where init_data implements Decoding -init_client_app_help = \json, app -> - state = - json - |> Decode.from_bytes(Json.json) - |> app.init - dynamic_view = - app.render(state) - static_unindexed = - translate_static(dynamic_view) - { nodes: static_nodes } = - index_nodes({ nodes: [], sibling_ids: [] }, static_unindexed) - static_rendered = { - root: List.len(static_nodes) - 1, - nodes: List.map(static_nodes, Ok), - deleted_node_cache: [], - handlers: [], - deleted_handler_cache: [], - } - - # Run our first diff. The only differences will be event listeners, so we will generate patches to attach those. - { rendered, patches } = - diff({ rendered: static_rendered, patches: [] }, dynamic_view) - - { state, rendered, patches } - -# Assign an index to each (virtual) DOM node. -# In JavaScript, we maintain an array of references to real DOM nodes. -# In Roc, we maintain a matching List of virtual DOM nodes with the same indices. -# They are both initialised separately, but use the same indexing algorithm. -# (We *could* pass this data in as JSON from the HTML file, but it would roughly double the size of that HTML file!) -index_nodes : { nodes : List RenderedNode, sibling_ids : List U64 }, Html state -> { nodes : List RenderedNode, sibling_ids : List U64 } -index_nodes = \{ nodes, sibling_ids }, unrendered -> - when unrendered is - Text(content) -> - { - nodes: List.append(nodes, RenderedText(content)), - sibling_ids: List.append(sibling_ids, List.len(nodes)), - } - - Element(name, _, attrs, children) -> - { nodes: list_with_children, sibling_ids: child_ids } = - List.walk(children, { nodes, sibling_ids: [] }, index_nodes) - rendered_attrs = - List.walk(attrs, empty_rendered_attrs, \walked_attrs, attr -> - when attr is - EventListener(_, _, _) -> walked_attrs # Dropped! Server-rendered HTML has no listeners - HtmlAttr(k, v) -> { walked_attrs & html_attrs: Dict.insert(walked_attrs.html_attrs, k, v) } - DomProp(k, v) -> { walked_attrs & dom_props: Dict.insert(walked_attrs.dom_props, k, v) } - Style(k, v) -> { walked_attrs & styles: Dict.insert(walked_attrs.styles, k, v) }) - - { - nodes: List.append(list_with_children, RenderedElement(name, rendered_attrs, child_ids)), - sibling_ids: List.append(sibling_ids, List.len(list_with_children)), - } - - None -> - { - nodes: List.append(nodes, RenderedNone), - sibling_ids: List.append(sibling_ids, List.len(nodes)), - } - -# ------------------------------- -# Patches -# ------------------------------- -apply_patch : Patch -> Task {} * -apply_patch = \patch -> - when patch is - CreateElement(node_id, tag_name) -> PlatformTasks.create_element(node_id, tag_name) - CreateTextNode(node_id, content) -> PlatformTasks.create_text_node(node_id, content) - UpdateTextNode(node_id, content) -> PlatformTasks.update_text_node(node_id, content) - AppendChild(parent_id, child_id) -> PlatformTasks.append_child(parent_id, child_id) - RemoveNode(id) -> PlatformTasks.remove_node(id) - ReplaceNode(old_id, new_id) -> PlatformTasks.replace_node(old_id, new_id) - SetAttribute(node_id, attr_name, value) -> PlatformTasks.set_attribute(node_id, attr_name, value) - RemoveAttribute(node_id, attr_name) -> PlatformTasks.remove_attribute(node_id, attr_name) - SetProperty(node_id, prop_name, json) -> PlatformTasks.set_property(node_id, prop_name, json) - RemoveProperty(node_id, prop_name) -> PlatformTasks.remove_property(node_id, prop_name) - SetStyle(node_id, key, value) -> PlatformTasks.set_style(node_id, key, value) - SetListener(node_id, event_type, accessors_json, handler_id) -> PlatformTasks.set_listener(node_id, event_type, accessors_json, handler_id) - RemoveListener(node_id, handler_id) -> PlatformTasks.remove_listener(node_id, handler_id) - -apply_patches : List Patch -> Task {} * -apply_patches = \patches -> - List.walk(patches, Task.ok({}), \previous_effects, patch -> - previous_effects! - apply_patch(patch)) - -# ------------------------------- -# EVENT HANDLING -# ------------------------------- -JsEventResult state init_data : { - platform_state : PlatformState state init_data, - stop_propagation : Bool, - prevent_default : Bool, -} - -## Dispatch a JavaScript event to a Roc handler, given the handler ID and some JSON event data. -dispatch_event : PlatformState state init_data, List (List U8), HandlerId -> Task (JsEventResult state init_data) * where init_data implements Decoding -dispatch_event = \platform_state, event_data, handler_id -> - { app, state, rendered } = - platform_state - maybe_handler = - List.get(rendered.handlers, handler_id) - |> Result.with_default(Err(DeletedHandler)) - { action, stop_propagation, prevent_default } = - when maybe_handler is - Err(DeletedHandler) -> - { action: Action.none, stop_propagation: Bool.false, prevent_default: Bool.false } - - Ok(Normal(handler)) -> - { action: handler(state, event_data), stop_propagation: Bool.false, prevent_default: Bool.false } - - Ok(Custom(handler)) -> - handler(state, event_data) - - when action is - Update(new_state) -> - new_view_unrendered = - app.render(new_state) - { rendered: new_rendered, patches } = - diff({ rendered, patches: [] }, new_view_unrendered) - - apply_patches!(patches) - Task.ok({ - platform_state: { - app, - state: new_state, - rendered: new_rendered, - }, - stop_propagation, - prevent_default, - }) - - None -> - Task.ok({ platform_state, stop_propagation, prevent_default }) - -# ------------------------------- -# DIFF -# ------------------------------- -diff : DiffState state, Html state -> DiffState state -diff = \{ rendered, patches }, new_node -> - root = - rendered.root - old_node = - List.get(rendered.nodes, root) - |> Result.with_default(Ok(RenderedNone)) - |> Result.with_default(RenderedNone) - - when { old_node, new_node } is - { old_node: RenderedText(old_content), new_node: Text(new_content) } -> - if new_content != old_content then - new_nodes = - List.set(rendered.nodes, rendered.root, Ok(RenderedText(new_content))) - - { - rendered: { rendered & - nodes: new_nodes, - }, - patches: List.append(patches, UpdateTextNode(rendered.root, new_content)), - } - else - { rendered, patches } - - { old_node: RenderedElement(old_name, old_attrs, old_children), new_node: Element(new_name, _, new_attrs, new_children) } -> - if new_name != old_name then - replace_node({ rendered, patches }, root, new_node) - else - state_attrs = - diff_attrs({ rendered, patches }, root, old_attrs, new_attrs) - state_child_pairs = - List.map2(old_children, new_children, \old_child_id, new_child -> { old_child_id, new_child }) - |> List.walk(state_attrs, \child_walk_state, { old_child_id, new_child } -> - { rendered: child_walk_rendered, patches: child_walk_patches } = child_walk_state - diff({ rendered: { child_walk_rendered & root: old_child_id }, patches: child_walk_patches }, new_child)) - { rendered: rendered_left_over_children, patches: patches_left_over_children } = - if List.len(old_children) > List.len(new_children) then - List.walk_from(old_children, List.len(new_children), state_child_pairs, delete_node) - else if List.len(old_children) < List.len(new_children) then - state_before_create = { - rendered: state_child_pairs.rendered, - patches: state_child_pairs.patches, - ids: [], - } - { rendered: rendered_after_create, patches: patches_after_create, ids: created_ids } = - List.walk_from(new_children, List.len(old_children), state_before_create, create_child_node) - # Look up the children again since they might have new node IDs! - node_with_updated_children = - when List.get(rendered_after_create.nodes, root) is - Ok(Ok(RenderedElement(n, a, c))) -> RenderedElement(n, a, List.concat(c, created_ids)) - _ -> crash("Bug in virtual-dom framework: nodeWithUpdatedChildren not found") - updated_nodes = - List.set(rendered_after_create.nodes, root, Ok(node_with_updated_children)) - - { - rendered: { rendered_after_create & nodes: updated_nodes }, - patches: List.walk(created_ids, patches_after_create, \p, id -> List.append(p, AppendChild(root, id))), - } - else - state_child_pairs - - { - rendered: { rendered_left_over_children & root }, - patches: patches_left_over_children, - } - - { old_node: RenderedNone, new_node: None } -> - { rendered, patches } - - _ -> - # old node has been replaced with a totally different variant. There's no point in diffing, just replace. - replace_node({ rendered, patches }, rendered.root, new_node) - -replace_node : DiffState state, NodeId, Html state -> DiffState state -replace_node = \diff_state, old_node_id, new_node -> - { rendered: create_rendered, patches: create_patches, id: create_node_id } = - create_node(diff_state, new_node) - pre_delete_state = { - rendered: create_rendered, - patches: List.append(create_patches, ReplaceNode(old_node_id, create_node_id)), - } - - delete_node(pre_delete_state, old_node_id) - -# Delete a node, and drop any JS references to its children and event listeners -# TODO: see if it would speed things up to leave this junk lying around until the slot is reused. -# Any danger of spurious events being sent to the wrong handler? -# Otherwise, can we sweep everything at once at the end of the diff? -# Let's be conservative on things like this until we have more test cases working. -delete_node : DiffState state, NodeId -> DiffState state -delete_node = \diff_state, id -> - { rendered, patches } = - when List.get(diff_state.rendered.nodes, id) is - Ok(node) -> - when node is - Ok(RenderedElement(_, _, children)) -> - List.walk(children, diff_state, delete_node) - - _ -> diff_state - - _ -> diff_state - - patches_remove_listeners = - when List.get(rendered.nodes, id) is - Ok(Ok(RenderedElement(_, attrs, _))) -> - Dict.walk(attrs.event_listeners, patches, \p, _, { handler_id } -> - List.append(p, RemoveListener(id, handler_id))) - - _ -> patches - - new_nodes = - List.set(rendered.nodes, id, Err(DeletedNode)) - new_deleted_node_cache = - List.append(rendered.deleted_node_cache, id) - new_patches = - List.append(patches_remove_listeners, RemoveNode(id)) - - { - rendered: { rendered & - nodes: new_nodes, - deleted_node_cache: new_deleted_node_cache, - }, - patches: new_patches, - } - -create_node : DiffState state, Html state -> { rendered : RenderedTree state, patches : List Patch, id : NodeId } -create_node = \{ rendered, patches }, new_node -> - when new_node is - Text(content) -> - { rendered: new_rendered, id } = - insert_node(rendered, RenderedText(content)) - - { - rendered: new_rendered, - patches: List.append(patches, CreateTextNode(id, content)), - id, - } - - None -> - { rendered: new_rendered, id } = - insert_node(rendered, RenderedNone) - - { rendered: new_rendered, patches, id } - - Element(tag_name, _, attrs, children) -> - { rendered: rendered_with_children, patches: patches_with_children, ids: child_ids } = - List.walk(children, { rendered, patches, ids: [] }, create_child_node) - node_id = - next_node_id(rendered_with_children) - patches_with_elem = - List.append(patches_with_children, CreateElement(node_id, tag_name)) - { rendered_attrs, rendered: rendered_with_attrs, patches: patches_with_attrs } = - render_attrs(attrs, rendered_with_children, patches_with_elem, node_id) - { rendered: rendered_with_node } = - insert_node(rendered_with_attrs, RenderedElement(tag_name, rendered_attrs, child_ids)) - - { - rendered: rendered_with_node, - patches: patches_with_attrs, - id: node_id, - } - -AttrDiffState state : { - node_id : NodeId, - attrs : RenderedAttributes, - patches : List Patch, - handlers : List (Result (Handler state) [DeletedHandler]), - deleted_handler_cache : List HandlerId, -} - -diff_attrs : DiffState state, NodeId, RenderedAttributes, List (Attribute state) -> DiffState state -diff_attrs = \{ rendered, patches }, node_id, attrs, new_attrs -> - init_state = { - node_id, - attrs, - patches, - handlers: rendered.handlers, - deleted_handler_cache: rendered.deleted_handler_cache, - } - final_state = - List.walk(new_attrs, init_state, diff_attr) - new_rendered = - { rendered & - handlers: final_state.handlers, - deleted_handler_cache: final_state.deleted_handler_cache, - } - - { - rendered: new_rendered, - patches: final_state.patches, - } - -diff_attr : AttrDiffState state, Attribute state -> AttrDiffState state -diff_attr = \{ node_id, attrs, patches, handlers, deleted_handler_cache }, attr -> - when attr is - EventListener(event_name, new_accessors, new_handler) -> - when Dict.get(attrs.event_listeners, event_name) is - Ok({ accessors, handler_id }) -> - Tuple(new_attrs, new_patches) = - if accessors == new_accessors then - Tuple(attrs, patches) - else - json = new_accessors |> Encode.to_bytes(Json.json) - - Tuple( - { attrs & event_listeners: Dict.insert(attrs.event_listeners, event_name, { accessors, handler_id }) }, - ( - patches - |> List.append(RemoveListener(node_id, handler_id)) - |> List.append(SetListener(node_id, event_name, json, handler_id)) - ), - ) - - { - node_id, - attrs: new_attrs, - patches: new_patches, - handlers: List.set(handlers, handler_id, Ok(new_handler)), - deleted_handler_cache, - } - - Err(KeyNotFound) -> - render_attr({ node_id, attrs, patches, handlers, deleted_handler_cache }, attr) - - HtmlAttr(k, v) -> - when Dict.get(attrs.html_attrs, k) is - Ok(old_val) -> - Tuple(new_attrs, new_patches) = - if old_val == v then - Tuple(attrs, patches) - else - Tuple( - { attrs & html_attrs: Dict.insert(attrs.html_attrs, k, v) }, - (patches |> List.append(SetAttribute(node_id, k, v))), - ) - { - node_id, - attrs: new_attrs, - patches: new_patches, - handlers, - deleted_handler_cache, - } - - Err(KeyNotFound) -> - render_attr({ node_id, attrs, patches, handlers, deleted_handler_cache }, attr) - - DomProp(k, v) -> - when Dict.get(attrs.dom_props, k) is - Ok(old_val) -> - Tuple(new_attrs, new_patches) = - if old_val == v then - Tuple(attrs, patches) - else - Tuple( - { attrs & dom_props: Dict.insert(attrs.dom_props, k, v) }, - (patches |> List.append(SetProperty(node_id, k, v))), - ) - { - node_id, - attrs: new_attrs, - patches: new_patches, - handlers, - deleted_handler_cache, - } - - Err(KeyNotFound) -> - render_attr({ node_id, attrs, patches, handlers, deleted_handler_cache }, attr) - - Style(k, v) -> - when Dict.get(attrs.styles, k) is - Ok(old_val) -> - Tuple(new_attrs, new_patches) = - if old_val == v then - Tuple(attrs, patches) - else - Tuple( - { attrs & styles: Dict.insert(attrs.styles, k, v) }, - (patches |> List.append(SetStyle(node_id, k, v))), - ) - { - node_id, - attrs: new_attrs, - patches: new_patches, - handlers, - deleted_handler_cache, - } - - Err(KeyNotFound) -> - render_attr({ node_id, attrs, patches, handlers, deleted_handler_cache }, attr) - -render_attrs : List (Attribute state), RenderedTree state, List Patch, NodeId -> { rendered_attrs : RenderedAttributes, rendered : RenderedTree state, patches : List Patch } -render_attrs = \attrs, rendered, patches, node_id -> - init_state = { - node_id, - attrs: empty_rendered_attrs, - patches, - handlers: rendered.handlers, - deleted_handler_cache: rendered.deleted_handler_cache, - } - final_state = - List.walk(attrs, init_state, render_attr) - - { - rendered_attrs: final_state.attrs, - rendered: { rendered & - handlers: final_state.handlers, - deleted_handler_cache: final_state.deleted_handler_cache, - }, - patches: final_state.patches, - } - -render_attr : AttrDiffState state, Attribute state -> AttrDiffState state -render_attr = \{ node_id, attrs, patches, handlers, deleted_handler_cache }, attr -> - when attr is - HtmlAttr(k, v) -> - { - node_id, - handlers, - deleted_handler_cache, - attrs: { attrs & html_attrs: Dict.insert(attrs.html_attrs, k, v) }, - patches: List.append(patches, SetAttribute(node_id, k, v)), - } - - DomProp(k, v) -> - { - node_id, - handlers, - deleted_handler_cache, - attrs: { attrs & dom_props: Dict.insert(attrs.dom_props, k, v) }, - patches: List.append(patches, SetProperty(node_id, k, v)), - } - - Style(k, v) -> - { - node_id, - handlers, - deleted_handler_cache, - attrs: { attrs & styles: Dict.insert(attrs.styles, k, v) }, - patches: List.append(patches, SetStyle(node_id, k, v)), - } - - EventListener(event_type, accessors, handler) -> - { handler_id, new_handlers, new_deleted_handler_cache } = - when List.last(deleted_handler_cache) is - Ok(id) -> - { - handler_id: id, - new_handlers: List.set(handlers, id, Ok(handler)), - new_deleted_handler_cache: List.drop_last(deleted_handler_cache, 1), - } - - Err(_) -> - { - handler_id: List.len(handlers), - new_handlers: List.append(handlers, Ok(handler)), - new_deleted_handler_cache: deleted_handler_cache, - } - accessors_json = - accessors |> Encode.to_bytes(Json.json) - patch = - SetListener(node_id, event_type, accessors_json, handler_id) - - { - node_id, - attrs: { attrs & event_listeners: Dict.insert(attrs.event_listeners, event_type, { accessors, handler_id }) }, - handlers: new_handlers, - deleted_handler_cache: new_deleted_handler_cache, - patches: List.append(patches, patch), - } - -create_child_node : - { rendered : RenderedTree state, patches : List Patch, ids : List NodeId }, - Html state - -> { rendered : RenderedTree state, patches : List Patch, ids : List NodeId } -create_child_node = \{ rendered, patches, ids }, child_html -> - { rendered: rendered_child, patches: child_patches, id } = - create_node({ rendered, patches }, child_html) - - { - rendered: rendered_child, - patches: child_patches, - ids: List.append(ids, id), - } - -# insert a node into the nodes list, assigning it a NodeId -insert_node : RenderedTree state, RenderedNode -> { rendered : RenderedTree state, id : NodeId } -insert_node = \rendered, node -> - when List.last(rendered.deleted_node_cache) is - Ok(id) -> - new_rendered = - { rendered & - nodes: List.set(rendered.nodes, id, Ok(node)), - deleted_node_cache: List.drop_last(rendered.deleted_node_cache, 1), - } - - { rendered: new_rendered, id } - - Err(_) -> - new_rendered = - { rendered & - nodes: List.append(rendered.nodes, Ok(node)), - } - - { rendered: new_rendered, id: List.len(rendered.nodes) } - -# Predict what NodeId will be assigned next, without actually assigning it -next_node_id : RenderedTree state -> NodeId -next_node_id = \rendered -> - when List.last(rendered.deleted_node_cache) is - Ok(id) -> id - Err(_) -> List.len(rendered.nodes) - -# ------------------------------- -# TESTS -# ------------------------------- -eq_rendered_tree : RenderedTree state, RenderedTree state -> Bool -eq_rendered_tree = \a, b -> - (a.root == b.root) - && (a.nodes == b.nodes) - && (List.len(a.handlers) == List.len(b.handlers)) - && (a.deleted_node_cache == b.deleted_node_cache) - && (a.deleted_handler_cache == b.deleted_handler_cache) - -# indexNodes -expect - html : Html {} - html = - Element("a", 43, [HtmlAttr("href", "https://www.roc-lang.org/")], [Text("Roc")]) - - actual : { nodes : List RenderedNode, sibling_ids : List U64 } - actual = - index_nodes({ nodes: [], sibling_ids: [] }, html) - - expected : { nodes : List RenderedNode, sibling_ids : List U64 } - expected = { - nodes: [ - RenderedText("Roc"), - RenderedElement("a", { empty_rendered_attrs & html_attrs: Dict.from_list([("href", "https://www.roc-lang.org/")]) }, [0]), - ], - sibling_ids: [1], - } - - (actual.nodes == expected.nodes) - && (actual.sibling_ids == expected.sibling_ids) - -# diff -expect - State : { answer : U32 } - - diff_state_before : DiffState State - diff_state_before = { - rendered: { - root: 4, - nodes: [ - Ok(RenderedText("The app")), - Ok(RenderedElement("h1", empty_rendered_attrs, [0])), - Ok(RenderedText("The answer is 42")), - Ok(RenderedElement("div", empty_rendered_attrs, [2])), - Ok(RenderedElement("body", empty_rendered_attrs, [1, 3])), - ], - deleted_node_cache: [], - handlers: [], - deleted_handler_cache: [], - }, - patches: [], - } - - # Sizes don't matter, use zero. We are not creating a HTML string so we don't care what size it would be. - new_node : Html State - new_node = - Element("body", 0, [], [ - Element("h1", 0, [], [Text("The app")]), - Element("div", 0, [], [Text("The answer is 111")]), - ]) - - expected : DiffState State - expected = { - rendered: { - root: 4, - nodes: [ - Ok(RenderedText("The app")), - Ok(RenderedElement("h1", empty_rendered_attrs, [0])), - Ok(RenderedText("The answer is 111")), - Ok(RenderedElement("div", empty_rendered_attrs, [2])), - Ok(RenderedElement("body", empty_rendered_attrs, [1, 3])), - ], - deleted_node_cache: [], - handlers: [], - deleted_handler_cache: [], - }, - patches: [UpdateTextNode(2, "The answer is 111")], - } - - actual : DiffState State - actual = - diff(diff_state_before, new_node) - - (actual.patches == expected.patches) - && eq_rendered_tree(actual.rendered, expected.rendered) - -# initClientAppHelp -expect - State : { answer : U32 } - - init = \result -> - when result is - Ok(state) -> state - Err(_) -> { answer: 0 } - - on_click_handler : Handler State - on_click_handler = - Normal(\state, _ -> Action.update({ answer: state.answer + 1 })) - - render : State -> Html State - render = \state -> - num = Num.to_str(state.answer) - - on_click_attr : Attribute State - on_click_attr = - EventListener("click", [], on_click_handler) - - # Sizes don't matter, use zero. We are not creating a HTML string so we don't care what size it would be. - Element("body", 0, [], [ - Element("h1", 0, [], [Text("The app")]), - Element("div", 0, [on_click_attr], [Text("The answer is $(num)")]), - ]) - - app : App State State - app = { - init, - render, - wasm_url: "assets/test.wasm", - } - - init_json : List U8 - init_json = - { answer: 42 } |> Encode.to_bytes(Json.json) # panics at mono/src/ir.rs:5739:56 - expected : { state : State, rendered : RenderedTree State, patches : List Patch } - expected = { - state: { answer: 42 }, - rendered: { - root: 4, - nodes: [ - Ok(RenderedText("The app")), - Ok(RenderedElement("h1", empty_rendered_attrs, [0])), - Ok(RenderedText("The answer is 42")), - Ok(RenderedElement("div", { empty_rendered_attrs & event_listeners: Dict.from_list([("click", { accessors: [], handler_id: 0 })]) }, [2])), - Ok(RenderedElement("body", empty_rendered_attrs, [1, 3])), - ], - deleted_node_cache: [], - handlers: [Ok(on_click_handler)], - deleted_handler_cache: [], - }, - patches: [SetListener(3, "click", [], 0)], - } - - actual : { state : State, rendered : RenderedTree State, patches : List Patch } - actual = - init_client_app_help(init_json, app) - - (actual.state == expected.state) - && eq_rendered_tree(actual.rendered, expected.rendered) - && (actual.patches == expected.patches) diff --git a/examples/virtual-dom-wip/platform/Html/Internal/Server.roc b/examples/virtual-dom-wip/platform/Html/Internal/Server.roc deleted file mode 100644 index 51ebc096506..00000000000 --- a/examples/virtual-dom-wip/platform/Html/Internal/Server.roc +++ /dev/null @@ -1,122 +0,0 @@ -module [ - append_rendered_static, - init_server_app, -] - -import Html.Internal.Shared exposing [Html, Attribute, App, translate_static, text, element] -import Json - -# ------------------------------- -# STATIC HTML -# ------------------------------- -append_rendered_static : Str, Html [] -> Str -append_rendered_static = \buffer, node -> - when node is - Text(content) -> - Str.concat(buffer, content) - - Element(name, _, attrs, children) -> - with_tag_name = "$(buffer)<$(name)" - with_attrs = - if List.is_empty(attrs) then - with_tag_name - else - init = { buffer: Str.concat(with_tag_name, " "), styles: "" } - { buffer: attr_buffer, styles } = - List.walk(attrs, init, append_rendered_static_attr) - - if Str.is_empty(styles) then - attr_buffer - else - "$(attr_buffer) style=\"$(styles)\"" - - with_tag = Str.concat(with_attrs, ">") - with_children = List.walk(children, with_tag, append_rendered_static) - - "$(with_children)" - - None -> buffer - -append_rendered_static_attr : { buffer : Str, styles : Str }, Attribute [] -> { buffer : Str, styles : Str } -append_rendered_static_attr = \{ buffer, styles }, attr -> - when attr is - HtmlAttr(key, value) -> - new_buffer = "$(buffer) $(key)=\"$(value)\"" - - { buffer: new_buffer, styles } - - Style(key, value) -> - new_styles = "$(styles) $(key): $(value);" - - { buffer, styles: new_styles } - - DomProp(_, _) -> { buffer, styles } - -# ------------------------------- -# INITIALISATION -# ------------------------------- -init_server_app : App state init_data, init_data, Str -> Result (Html []) [InvalidDocument] where init_data implements Encoding -init_server_app = \app, init_data, host_java_script -> - init_data - |> Ok - |> app.init - |> app.render - |> translate_static - |> insert_roc_script(init_data, app.wasm_url, host_java_script) - -insert_roc_script : Html [], init_data, Str, Str -> Result (Html []) [InvalidDocument] where init_data implements Encoding -insert_roc_script = \document, init_data, wasm_url, host_java_script -> - encode = - \value -> - value - |> Encode.to_bytes(Json.json) - |> Str.from_utf8 - |> Result.with_default("") - - # Convert initData to JSON as a Roc Str, then convert the Roc Str to a JS string. - # JSON won't have invalid UTF-8 in it, since it would be escaped as part of JSON encoding. - js_init_data = - init_data |> encode |> encode - - js_wasm_url = - encode(wasm_url) - - script : Html [] - script = element("script")([], [ - text( - """ - $(host_java_script) - (function(){ - const initData = $(js_init_data); - const wasmUrl = $(js_wasm_url); - window.roc = roc_init(initData, wasmUrl); - })(); - """, - ), - ]) - - # append the