WIP try simplification

This commit is contained in:
Luke Boswell 2024-04-08 22:18:45 +10:00
parent 85b76a14b4
commit 8b9e08bd76
No known key found for this signature in database
GPG key ID: F6DB3C9DB47377B0
13 changed files with 734 additions and 551 deletions

View file

@ -2494,6 +2494,7 @@ pub fn can_defs_with_return<'a>(
if !output.references.has_type_or_value_lookup(symbol)
&& !scope.abilities_store.is_specialization_name(symbol)
{
dbg!(&symbol, &region);
env.problem(Problem::UnusedDef(symbol, region));
}
}

View file

@ -1,6 +1,6 @@
#![allow(clippy::manual_map)]
use crate::suffixed::unwrap_suffixed_expression;
use crate::suffixed::{apply_task_await, unwrap_suffixed_expression, EUnwrapped};
use bumpalo::collections::Vec;
use bumpalo::Bump;
use roc_error_macros::internal_error;
@ -168,7 +168,7 @@ pub fn desugar_defs_node_values<'a>(
// been desugared
if top_level_def {
for value_def in defs.value_defs.iter_mut() {
*value_def = desugar_value_def_suffixed(arena, *value_def);
*value_def = dbg!(desugar_value_def_suffixed(arena, *value_def));
}
}
}
@ -183,13 +183,20 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>)
match value_def {
Body(loc_pattern, loc_expr) => {
// note called_from_def is passed as `false` as this is a top_level_def
let result = unwrap_suffixed_expression(arena, loc_expr, false);
match result {
match unwrap_suffixed_expression(arena, loc_expr, None) {
Ok(new_expr) => Body(loc_pattern, new_expr),
Err(err) => {
internal_error!("this should have been desugared elsewhere... {:?}", err)
}
Err(EUnwrapped::UnwrappedSubExpr {
sub_arg,
sub_pat,
sub_new,
}) => Body(
loc_pattern,
apply_task_await(&arena, loc_expr.region, sub_arg, sub_pat, sub_new),
),
Err(..) => Body(
loc_pattern,
arena.alloc(Loc::at(loc_expr.region, MalformedSuffixed(loc_expr))),
),
}
}
ann @ Annotation(_, _) => ann,
@ -201,9 +208,7 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>)
body_expr,
} => {
// note called_from_def is passed as `false` as this is a top_level_def
let result = unwrap_suffixed_expression(arena, body_expr, false);
match result {
match unwrap_suffixed_expression(arena, body_expr, None) {
Ok(new_expr) => AnnotatedBody {
ann_pattern,
ann_type,
@ -211,8 +216,23 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>)
body_pattern,
body_expr: new_expr,
},
Err(err) => {
internal_error!("this should have been desugared elsewhere... {:?}", err)
Err(EUnwrapped::UnwrappedSubExpr {
sub_arg,
sub_pat,
sub_new,
}) => AnnotatedBody {
ann_pattern,
ann_type,
comment,
body_pattern,
body_expr: apply_task_await(&arena, body_expr.region, sub_arg, sub_pat, sub_new),
},
Err(..) => AnnotatedBody {
ann_pattern,
ann_type,
comment,
body_pattern,
body_expr: arena.alloc(Loc::at(body_expr.region, MalformedSuffixed(body_expr))),
}
}
}
@ -247,6 +267,7 @@ pub fn desugar_expr<'a>(
| Underscore { .. }
| MalformedIdent(_, _)
| MalformedClosure
| MalformedSuffixed(..)
| PrecedenceConflict { .. }
| MultipleRecordBuilders { .. }
| UnappliedRecordBuilder { .. }

View file

@ -1377,6 +1377,10 @@ pub fn canonicalize_expr<'a>(
(RuntimeError(problem), Output::default())
}
ast::Expr::MalformedSuffixed(..) => {
use roc_problem::can::RuntimeError::*;
(RuntimeError(MalformedSuffixed(region)), Output::default())
}
ast::Expr::MultipleRecordBuilders(sub_expr) => {
use roc_problem::can::RuntimeError::*;
@ -2449,6 +2453,7 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
.iter()
.all(|loc_field| is_valid_interpolation(&loc_field.value)),
ast::Expr::MultipleRecordBuilders(loc_expr)
| ast::Expr::MalformedSuffixed(loc_expr)
| ast::Expr::UnappliedRecordBuilder(loc_expr)
| ast::Expr::PrecedenceConflict(PrecedenceConflict { expr: loc_expr, .. })
| ast::Expr::UnaryOp(loc_expr, _)

View file

@ -43,22 +43,45 @@ fn next_suffixed_answer_pattern(arena: &Bump) -> (Expr, Pattern) {
#[derive(Debug)]
pub enum EUnwrapped<'a> {
/// The expression was unwrapped, consumes the def and following expression
/// e.g. `x = foo!` unwrapped `Task.await (foo) \x -> ...`
UnwrappedExpr(&'a Loc<Expr<'a>>),
UnwrappedDefExpr(&'a Loc<Expr<'a>>),
/// The expression had a (sub) expression unwrapped, doesn't affect the def or following expression
/// e.g. `x = bar! (foo!)` unwrapped `x = Task.await (foo) \#answer1 -> bar! #answer1`
UnwrappedSubExpr {
/// this is the unwrapped suffixed which will be applied to the Task.await
arg: &'a Loc<Expr<'a>>,
/// the unwrapped expression argument for Task.await
sub_arg: &'a Loc<Expr<'a>>,
/// this pattern will be used in the closure
pat: Loc<Pattern<'a>>,
/// the pattern for the closure
sub_pat: &'a Loc<Pattern<'a>>,
/// this expression will replace the suffixed in the parent
new: &'a Loc<Expr<'a>>,
/// the expression to replace the unwrapped
sub_new: &'a Loc<Expr<'a>>,
},
Malformed,
}
fn init_unwrapped_err<'a>(
arena: &'a Bump,
maybe_def_pat: Option<&'a Loc<Pattern<'a>>>,
unwrapped_expr: &'a Loc<Expr<'a>>,
) -> Result<&'a Loc<Expr<'a>>, 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(unwrapped_expr))
}
None => {
let (answer_var, answer_pat) = next_suffixed_answer_pattern(arena);
let sub_new = arena.alloc(Loc::at(unwrapped_expr.region, answer_var));
let sub_pat = arena.alloc(Loc::at(unwrapped_expr.region, answer_pat));
Err(EUnwrapped::UnwrappedSubExpr {
sub_arg: unwrapped_expr,
sub_pat,
sub_new,
})
}
}
}
/// Descend through the AST and unwrap each suffixed expression
@ -69,10 +92,16 @@ pub fn unwrap_suffixed_expression<'a>(
arena: &'a Bump,
loc_expr: &'a Loc<Expr<'a>>,
// was this function called from e.g. ValueDef::Body
// we need this to handle the case where we cannot unwrap the expression
called_from_def: bool,
// passed in from the def, first may have a pattern like "x = foo!" to unwrap
//
// note be careful with calls to this function
// only those from the defs helper should pass a `Some`
// only those to helpers should forward the value
// all other calls to sub-expressions should pass `None`
maybe_def_pat: Option<&'a Loc<Pattern<'a>>>,
) -> Result<&'a Loc<Expr<'a>>, EUnwrapped<'a>> {
dbg!("unwrap_suffixed_expression", &loc_expr);
match loc_expr.value {
Expr::Var { suffixed, .. } if suffixed == 0 => Ok(loc_expr),
@ -81,69 +110,34 @@ pub fn unwrap_suffixed_expression<'a>(
ident,
suffixed,
} if suffixed > 0 => {
if suffixed > 1 {
/*
## Example with multiple suffix
foo!!
bar
let unwrapped_var = arena.alloc(Loc::at(
loc_expr.region,
Expr::Var {
module_name,
ident,
suffixed: suffixed.saturating_sub(1),
},
));
Task.await (foo) \#!a1 ->
(#!a1)!
bar
Task.await (foo) \#!a1 ->
Task.await #!a1 \{} -> bar
*/
let (mut answer_var, answer_pat) = next_suffixed_answer_pattern(arena);
let arg = arena.alloc(Loc::at(
loc_expr.region,
Var {
module_name,
ident,
suffixed: 0,
},
));
let pat = Loc::at(loc_expr.region, answer_pat);
// we increment the suffix of the answer variable as there may be more than 1 additional suffix
answer_var.increment_var_suffix(suffixed.saturating_sub(1));
let new = arena.alloc(Loc::at(loc_expr.region, answer_var));
return Err(EUnwrapped::UnwrappedSubExpr { arg, pat, new });
}
if called_from_def {
Err(EUnwrapped::UnwrappedExpr(arena.alloc(Loc::at(
loc_expr.region,
Expr::Var {
module_name,
ident,
suffixed: suffixed.saturating_sub(1),
},
))))
} else {
internal_error!("unwrapping Var {:?} within a sub expression... this should have been handled at the previous level", loc_expr);
}
init_unwrapped_err(arena, maybe_def_pat, unwrapped_var)
}
Expr::Defs(..) => unwrap_suffixed_expression_defs_help(arena, loc_expr, called_from_def),
Expr::Defs(..) => unwrap_suffixed_expression_defs_help(arena, loc_expr, maybe_def_pat),
Expr::Apply(..) => unwrap_suffixed_expression_apply_help(arena, loc_expr, called_from_def),
Expr::Apply(..) => unwrap_suffixed_expression_apply_help(arena, loc_expr, maybe_def_pat),
Expr::SpaceBefore(..) | Expr::SpaceAfter(..) => {
internal_error!("unexpected SpaceBefore or SpaceAfter in unwrap_suffixed_expression, should have been removed in desugar_expr before this");
}
Expr::When(..) => unwrap_suffixed_expression_when_help(arena, loc_expr, called_from_def),
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, called_from_def)
unwrap_suffixed_expression_if_then_else_help(arena, loc_expr, maybe_def_pat)
}
Expr::BinOps(..) => internal_error!("BinOps should have been desugared in desugar_expr"),
Expr::Closure(args, return_expr) => {
let count_suffixed_args = args
.iter()
@ -156,55 +150,85 @@ pub fn unwrap_suffixed_expression<'a>(
}
// check the return expression
match unwrap_suffixed_expression(arena, return_expr, false) {
match unwrap_suffixed_expression(arena, return_expr, None) {
Ok(new_expr) => {
Ok(arena.alloc(Loc::at(loc_expr.region, Expr::Closure(args, new_expr))))
}
Err(..) => {
// TODO make this a nicer error
/*
Can we have suffixed expressions in a closure return expression?
x = \msg -> line! msg
x "hi"
*/
internal_error!(
"unexpected unwrapped closure return expression, how is this possible?"
);
Err(EUnwrapped::UnwrappedDefExpr(..)) => {
internal_error!("is this even possible? we aren't a Def");
}
Err(EUnwrapped::UnwrappedSubExpr {
sub_arg,
sub_pat,
sub_new,
}) => {
let new_closure =
arena.alloc(Loc::at(loc_expr.region, Expr::Closure(args, sub_new)));
return Err(EUnwrapped::UnwrappedSubExpr {
sub_arg,
sub_pat,
sub_new: new_closure,
});
}
Err(err) => Err(err),
}
}
Expr::ParensAround(sub_loc_expr) => {
match unwrap_suffixed_expression(
arena,
arena.alloc(Loc::at_zero(*sub_loc_expr)),
called_from_def,
) {
Ok(new_expr) => Ok(arena.alloc(Loc::at(
loc_expr.region,
ParensAround(arena.alloc(new_expr.value)),
))),
Err(EUnwrapped::UnwrappedExpr(new_expr)) => {
Err(EUnwrapped::UnwrappedExpr(arena.alloc(Loc::at(
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)),
))))
));
return Ok(new_parens);
}
Err(EUnwrapped::UnwrappedSubExpr { arg, pat, new }) => {
Err(EUnwrapped::UnwrappedSubExpr {
arg,
pat,
new: arena.alloc(Loc::at(
loc_expr.region,
ParensAround(arena.alloc(new.value)),
)),
})
Err(EUnwrapped::UnwrappedDefExpr(..)) => {
internal_error!("is this even possible? we aren't a Def");
}
// // Err(EUnwrapped::UnwrappedExpr(arena.alloc(Loc::at(
// // loc_expr.region,
// // ParensAround(arena.alloc(new_expr.value)),
// // ))))
// let new_parens_expr = arena.alloc(Loc::at(
// new_expr.region,
// Expr::ParensAround(&new_expr.value),
// ));
// if called_from_def {
// return Err(EUnwrapped::UnwrappedExpr(new_parens_expr));
// } else {
// dbg!("UnwrappedSubExpr from ParensAround");
// let (answer_var, answer_pat) = next_suffixed_answer_pattern(arena);
// let sub_pat = Loc::at(new_expr.region, answer_pat);
// return Err(EUnwrapped::PassBackToDef {
// sub_arg: new_parens_expr,
// sub_pat,
// sub_new: arena.alloc(Loc::at(new_expr.region, answer_var)),
// });
// }
// }
Err(EUnwrapped::UnwrappedSubExpr {
sub_arg,
sub_pat,
sub_new,
}) => {
let new_parens = arena.alloc(Loc::at(
loc_expr.region,
ParensAround(arena.alloc(sub_new.value)),
));
return Err(EUnwrapped::UnwrappedSubExpr {
sub_arg,
sub_pat,
sub_new: new_parens,
});
}
Err(err) => Err(err),
}
}
@ -218,119 +242,111 @@ pub fn unwrap_suffixed_expression<'a>(
pub fn unwrap_suffixed_expression_apply_help<'a>(
arena: &'a Bump,
loc_expr: &'a Loc<Expr<'a>>,
called_from_def: bool,
// passed in from the def, first may have a pattern like "x = foo!" to unwrap
//
// note be careful with calls to this function
// only those from the defs helper should pass a `Some`
// only those to helpers should forward the value
// all other calls to sub-expressions should pass `None`
maybe_def_pat: Option<&'a Loc<Pattern<'a>>>,
) -> Result<&'a Loc<Expr<'a>>, 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().enumerate() {
/*
## Example of suffixed function within an Apply
x = bar (foo! "hello")
baz x
x = Task.await (foo "hello") \#!a -> bar (#!a)
baz x
*/
// try to unwrap the argument
match unwrap_suffixed_expression(arena, arg, false) {
match unwrap_suffixed_expression(arena, arg, None) {
Ok(new_arg) => {
*arg = new_arg;
},
Err(..) => {
todo!();
}
Err(EUnwrapped::UnwrappedDefExpr(..)) => {
internal_error!("is this even possible? we aren't a Def");
// let (answer_var, answer_pat) = next_suffixed_answer_pattern(arena);
// let sub_pat = Loc::at(loc_expr.region, answer_pat);
// *arg = arena.alloc(Loc::at(loc_expr.region, answer_var));
// let new_apply = arena.alloc(Loc::at(loc_expr.region, Apply(function, local_args, called_via)));
// return Err(EUnwrapped::PassBackToDef { sub_arg: new_expr, sub_pat, sub_new: new_apply });
}
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new: new_arg }) => {
*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});
// if called_from_def {
// *arg = sub_new;
// 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 })
// } else {
// *arg = sub_new;
// let new_apply = arena.alloc(Loc::at(loc_expr.region, Apply(function, local_args, called_via)));
// return unwrap_suffixed_expression(arena, apply_task_await(arena, loc_expr.region, sub_arg, sub_pat, new_apply), called_from_def);
// }
}
Err(err) => return Err(err),
}
}
// try to unwrap the function
// function is a Var
if let Loc { value: Var{module_name, ident,suffixed},region } = function {
match suffixed {
0 => {
// nothing to unwrap, move on to the return expression
return Ok(arena.alloc(Loc::at(loc_expr.region, Expr::Apply(function, local_args, called_via))));
},
1 => {
if called_from_def {
let new_apply = Loc::at(
loc_expr.region,
Expr::Apply(
arena.alloc(Loc::at(
loc_expr.region,
Var {
module_name,
ident,
suffixed: 0,
},
)),
local_args,
called_via,
),
);
return Err(EUnwrapped::UnwrappedExpr(arena.alloc(new_apply)));
} else {
/*
## Example of suffixed function within an Apply
x = (foo! "bar") "hello"
^^^^^^^^^^^^ is our suffixed function
baz x
Task.await (foo "bar") \#!a1 ->
x = #!a1 "hello"
baz x
*/
let (answer_var, answer_pat) = next_suffixed_answer_pattern(arena);
let arg = arena.alloc(Loc::at(loc_expr.region, Apply(
arena.alloc(Loc::at(
*region,
Var {
module_name,
ident,
suffixed: 0,
},
)),
local_args,
called_via,
)));
let pat = Loc::at(loc_expr.region, answer_pat);
let new = arena.alloc(Loc::at(loc_expr.region, answer_var));
return Err(EUnwrapped::UnwrappedSubExpr { arg, pat, new });
}
}
_ => {
internal_error!("TODO support multiple suffixes");
}
}
}
// handle other cases
match unwrap_suffixed_expression(arena, function, false) {
// function is another expression
match unwrap_suffixed_expression(arena, function, None) {
Ok(new_function) => {
return Ok(arena.alloc(Loc::at(loc_expr.region, Expr::Apply(new_function, local_args, called_via))));
},
Err(EUnwrapped::UnwrappedExpr(..)) => {
todo!();
let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(new_function, local_args, called_via)));
return Ok(new_apply);
}
Err(EUnwrapped::UnwrappedSubExpr { arg, pat, new }) => {
Err(EUnwrapped::UnwrappedDefExpr(..)) => {
internal_error!("is this even possible? we aren't a Def");
// if called_from_def {
// let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(new_function, local_args, called_via)));
// return Err(EUnwrapped::UnwrappedExpr(new_apply));
// } else {
// let (answer_var, answer_pat) = next_suffixed_answer_pattern(arena);
return Err(EUnwrapped::UnwrappedSubExpr {
arg,
pat,
new: arena.alloc(Loc::at(loc_expr.region, Expr::Apply(new, local_args, called_via)))
});
// let sub_pat = Loc::at(loc_expr.region, answer_pat);
// let function_answer = arena.alloc(Loc::at(loc_expr.region, answer_var));
// let new_apply= arena.alloc(Loc::at(loc_expr.region, Expr::Apply(function_answer, local_args, called_via)));
// return unwrap_suffixed_expression(
// arena,
// apply_task_await(arena, loc_expr.region, new_function, sub_pat, new_apply),
// false
// );
// // return Err(EUnwrapped::UnwrappedSubExpr {
// // sub_arg: arena.alloc(Loc::at(loc_expr.region, Expr::Apply(new_function, local_args, called_via))),
// // sub_pat,
// // sub_new: arena.alloc(Loc::at(new_function.region, answer_var))
// // });
// }
}
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new: new_function }) => {
let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(new_function, local_args, called_via)));
return Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new: new_apply});
// let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(sub_new, local_args, called_via)));
// return unwrap_suffixed_expression(arena,
// apply_task_await(arena, loc_expr.region, sub_arg, sub_pat, new_apply), false);
}
Err(err) => Err(err)
}
}
_ => internal_error!("unreachable, expected an Apply node to be passed into unwrap_suffixed_expression_apply_help"),
@ -340,7 +356,7 @@ pub fn unwrap_suffixed_expression_apply_help<'a>(
pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
_arena: &'a Bump,
loc_expr: &'a Loc<Expr<'a>>,
_called_from_def: bool, // TODO do we need this? pretty sure we do
_maybe_def_pat: Option<&'a Loc<Pattern<'a>>>,
) -> Result<&'a Loc<Expr<'a>>, EUnwrapped<'a>> {
Ok(loc_expr)
@ -471,7 +487,7 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
pub fn unwrap_suffixed_expression_when_help<'a>(
_arena: &'a Bump,
loc_expr: &'a Loc<Expr<'a>>,
_called_from_def: bool, // TODO do we need this? pretty sure we do
_maybe_def_pat: Option<&'a Loc<Pattern<'a>>>,
) -> Result<&'a Loc<Expr<'a>>, EUnwrapped<'a>> {
Ok(loc_expr)
}
@ -479,30 +495,28 @@ pub fn unwrap_suffixed_expression_when_help<'a>(
pub fn unwrap_suffixed_expression_defs_help<'a>(
arena: &'a Bump,
loc_expr: &'a Loc<Expr<'a>>,
_called_from_def: bool, // TODO do we need this? pretty sure we do
maybe_def_pat: Option<&'a Loc<Pattern<'a>>>,
) -> Result<&'a Loc<Expr<'a>>, EUnwrapped<'a>> {
match loc_expr.value {
Expr::Defs(defs, loc_ret) => {
let mut local_defs = defs.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) in defs.defs().enumerate() {
use ValueDef::*;
let maybe_pattern_expr = match type_or_value_def.err() {
let maybe_suffixed_value_def = match type_or_value_def.err() {
None | Some(Annotation(..)) | Some(Dbg{..}) | Some(Expect{..}) | Some(ExpectFx{..}) | Some(Stmt(..))=> None,
Some(AnnotatedBody { body_pattern, body_expr, .. }) => Some((body_pattern, body_expr)),
Some(Body (def_pattern, def_expr, .. )) => Some((def_pattern, def_expr)),
};
match maybe_pattern_expr {
match maybe_suffixed_value_def {
None => {
// nothing to unwrap, move on to the next def
// not a def that we can unwrap, move on to the next
continue;
},
Some((def_pattern, def_expr)) => {
match unwrap_suffixed_expression(arena, def_expr, true) {
match unwrap_suffixed_expression(arena, def_expr, Some(*def_pattern)) {
Ok(new_def_expr) => {
let new_value_def = match type_or_value_def.err() {
@ -511,129 +525,92 @@ pub fn unwrap_suffixed_expression_defs_help<'a>(
Some(Body ( .. )) => ValueDef::Body(def_pattern, new_def_expr),
};
// do nothing, move on to check the next def
let mut local_defs = defs.clone();
local_defs.replace_with_value_def(tag_index, new_value_def, loc_expr.region);
let new_defs = arena.alloc(Loc::at(def_expr.region, Defs(arena.alloc(local_defs), loc_ret)));
return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat);
}
Err(EUnwrapped::UnwrappedExpr(new)) => {
let split_defs = defs.split_defs_around(tag_index);
Err(EUnwrapped::UnwrappedDefExpr(unwrapped_expr)) => {
let local_defs = defs.clone();
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
return unwrap_suffixed_expression(
arena,
apply_task_await(
arena,
def_expr.region,
new,
**def_pattern,
loc_ret,
),
false,
);
let replaced_def = apply_task_await(arena,def_expr.region,unwrapped_expr,*def_pattern,loc_ret);
return unwrap_suffixed_expression(arena,replaced_def,maybe_def_pat);
} else if before_empty {
// NIL before, SOME after -> FIRST
return unwrap_suffixed_expression(
arena,
apply_task_await(arena, loc_expr.region, new, **def_pattern, arena.alloc(Loc::at(def_expr.region, Defs(arena.alloc(split_defs.after), loc_ret)))),
false,
);
let new_defs = arena.alloc(Loc::at(def_expr.region, Defs(arena.alloc(split_defs.after), loc_ret)));
let replaced_def = apply_task_await(arena, loc_expr.region, unwrapped_expr, *def_pattern, new_defs);
return unwrap_suffixed_expression(arena,replaced_def,maybe_def_pat);
} else {
// SOME before, NIL after -> LAST
debug_assert!(after_empty);
/*
## Example with multiple defs, last suffixed
msg = "hello"
x = foo! msg <-- e.g. we've just unwrapped this def
bar x
msg = "hello"
Task.await (foo msg) \x -> bar x
*/
let new_loc_ret = apply_task_await(
arena,
loc_expr.region,
new,
**def_pattern,
loc_ret,
);
return unwrap_suffixed_expression(
arena,
arena.alloc(Loc::at(loc_expr.region,
Defs(arena.alloc(split_defs.before), new_loc_ret))),
false,
);
let new_loc_ret = apply_task_await(arena,loc_expr.region,unwrapped_expr,*def_pattern,loc_ret);
let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), new_loc_ret)));
return unwrap_suffixed_expression(arena,new_defs,maybe_def_pat);
}
}
Err(EUnwrapped::UnwrappedSubExpr { arg, pat, new }) => {
let new_value_def = ValueDef::Body(def_pattern, new);
let mut new_defs = defs.clone();
new_defs.replace_with_value_def(
tag_index,
new_value_def,
def_expr.region,
);
return unwrap_suffixed_expression(
arena,
apply_task_await(
arena,
def_expr.region,
arg,
pat,
arena.alloc(Loc::at(
def_expr.region,
Defs(arena.alloc(new_defs), loc_ret),
)),
),
false,
);
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => {
let mut local_defs = defs.clone();
let new_body_def = ValueDef::Body(def_pattern, sub_new);
local_defs.replace_with_value_def(tag_index,new_body_def,def_expr.region);
let new_defs_expr = arena.alloc(Loc::at(def_expr.region,Defs(arena.alloc(local_defs), loc_ret)));
let replaced_def = apply_task_await(arena,def_expr.region,sub_arg,sub_pat,new_defs_expr);
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, false) {
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::UnwrappedExpr(new_loc_ret)) => {
// the loc_ret was unwrapped, replace and return new expression
Err(EUnwrapped::UnwrappedExpr(arena.alloc(Loc::at(loc_expr.region, Defs(defs, new_loc_ret)))))
Ok(arena.alloc(Loc::at(loc_expr.region, Defs(arena.alloc(defs), new_loc_ret))))
}
Err(EUnwrapped::UnwrappedSubExpr { arg, pat, new }) => {
Err(EUnwrapped::UnwrappedDefExpr(new_loc_ret)) => {
// the loc_ret was unwrapped, replace and return new expression
internal_error!("is this even possible? we are in the loc_ret of a Def");
// Err(EUnwrapped::UnwrappedDefExpr(arena.alloc(Loc::at(loc_expr.region, Defs(defs, new_loc_ret)))))
}
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => {
// the loc_ret was unwrapped
let new_loc_ret = apply_task_await(arena, loc_ret.region, arg, pat, new);
Err(EUnwrapped::UnwrappedExpr(arena.alloc(Loc::at(loc_expr.region, Defs(defs, new_loc_ret)))))
},
internal_error!("is this even possible? we are in the loc_ret of a Def");
// let new_loc_ret = apply_task_await(arena, loc_ret.region, sub_arg, sub_pat, sub_new);
// Err(EUnwrapped::UnwrappedDefExpr(arena.alloc(Loc::at(loc_expr.region, Defs(defs, new_loc_ret)))))
}
Err(err) => Err(err)
}
},
_ => internal_error!("unreachable, expected a Defs node to be passed into unwrap_suffixed_expression_defs_help"),
}
}
fn apply_task_await<'a>(
/// Helper for `Task.await (loc_arg) \loc_pat -> loc_new`
pub fn apply_task_await<'a>(
arena: &'a Bump,
region: Region,
arg_loc_expr: &'a Loc<Expr<'a>>,
loc_pat: Loc<Pattern<'a>>,
new: &'a Loc<Expr<'a>>,
loc_arg: &'a Loc<Expr<'a>>,
loc_pat: &'a Loc<Pattern<'a>>,
loc_new: &'a Loc<Expr<'a>>,
) -> &'a Loc<Expr<'a>> {
let mut task_await_apply_args: Vec<&'a Loc<Expr<'a>>> = Vec::new_in(arena);
// apply the unwrapped suffixed expression
task_await_apply_args.push(arg_loc_expr);
task_await_apply_args.push(loc_arg);
// apply the closure
let mut closure_pattern = Vec::new_in(arena);
closure_pattern.push(loc_pat);
closure_pattern.push(*loc_pat);
task_await_apply_args.push(arena.alloc(Loc::at(
region,
Closure(arena.alloc_slice_copy(closure_pattern.as_slice()), new),
Closure(arena.alloc_slice_copy(closure_pattern.as_slice()), loc_new),
)));
// e.g. `Task.await (arg_loc_expr) \pattern -> new`
arena.alloc(Loc::at(
region,
Apply(
@ -650,252 +627,3 @@ fn apply_task_await<'a>(
),
))
}
#[cfg(test)]
mod unwrap_suffixed {
use crate::desugar::desugar_defs_node_values;
use bumpalo::Bump;
use roc_parse::test_helpers::parse_defs_with;
use roc_test_utils::assert_multiline_str_eq;
#[test]
fn multi_defs_stmts() {
/*
line! "Ahoy"
{} = "There" |> Stdout.line!
Task.ok {}
# desugared
Task.await (line "Ahoy") \{} ->
Task.await ("There" |> Stdout.line) \{} ->
Task.ok {}
*/
let arena = &Bump::new();
let src = r#"
main =
line! "Ahoy"
{} = "There" |> Stdout.line!
Task.ok {}
"#;
let mut defs = parse_defs_with(arena, src).unwrap();
desugar_defs_node_values(arena, &mut defs, src, &mut None, "test.roc", true);
let expected = r#"Defs { tags: [Index(2147483648)], regions: [@0-36], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "main", suffixed: 0 }, @0-36 Apply(@0-36 Var { module_name: "Task", ident: "await", suffixed: 0 }, [@24-36 Apply(@24-36 Var { module_name: "", ident: "line", suffixed: 0 }, [@30-36 Str(PlainLine("Ahoy"))], Space), @0-36 Closure([@24-36 RecordDestructure([])], @58-81 Apply(@58-81 Var { module_name: "Task", ident: "await", suffixed: 0 }, [@58-81 Apply(@58-81 Var { module_name: "Stdout", ident: "line", suffixed: 0 }, [@58-65 Str(PlainLine("There"))], BinOp(Pizza)), @58-81 Closure([@53-55 RecordDestructure([])], @115-125 Apply(@115-122 Var { module_name: "Task", ident: "ok", suffixed: 0 }, [@123-125 Record([])], Space))], BangSuffix))], BangSuffix))] }"#;
assert_multiline_str_eq!(format!("{:?}", &defs).as_str(), expected);
}
#[test]
fn simple_pizza() {
/*
"hello" |> line!
Task.ok {}
# desugared
Task.await ("hello" |> line) \{} -> Task.ok {}
*/
let arena = &Bump::new();
let src = r#"
main =
"hello" |> line!
Task.ok {}
"#;
let mut defs = parse_defs_with(arena, src).unwrap();
desugar_defs_node_values(arena, &mut defs, src, &mut None, "test.roc", true);
let expected = r#"Defs { tags: [Index(2147483648)], regions: [@0-40], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "main", suffixed: 0 }, @24-40 Apply(@24-40 Var { module_name: "Task", ident: "await", suffixed: 0 }, [@24-40 Apply(@24-40 Var { module_name: "", ident: "line", suffixed: 0 }, [@24-31 Str(PlainLine("hello"))], BinOp(Pizza)), @24-40 Closure([@24-40 RecordDestructure([])], @58-68 Apply(@58-65 Var { module_name: "Task", ident: "ok", suffixed: 0 }, [@66-68 Record([])], Space))], BangSuffix))] }"#;
assert_multiline_str_eq!(format!("{:?}", &defs).as_str(), expected);
}
#[test]
fn single_suffix() {
/*
## Example with single suffix
x = foo!
bar x
# desugared
Task.await (foo) \x -> bar x
*/
let arena = &Bump::new();
let src = r#"
main =
x = foo!
bar x
"#;
let mut defs = parse_defs_with(arena, src).unwrap();
desugar_defs_node_values(arena, &mut defs, src, &mut None, "test.roc", true);
let expected = r#"Defs { tags: [Index(2147483648)], regions: [@0-54], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "main", suffixed: 0 }, @28-32 Apply(@28-32 Var { module_name: "Task", ident: "await", suffixed: 0 }, [@28-32 Var { module_name: "", ident: "foo", suffixed: 0 }, @28-32 Closure([@24-25 Identifier { ident: "x", suffixed: 0 }], @49-54 Apply(@49-52 Var { module_name: "", ident: "bar", suffixed: 0 }, [@53-54 Var { module_name: "", ident: "x", suffixed: 0 }], Space))], BangSuffix))] }"#;
assert_multiline_str_eq!(format!("{:?}", &defs).as_str(), expected);
}
#[test]
fn multiple_suffix() {
/*
## Example with multiple suffix
foo!!
bar
# step 1
Task.await (foo) \#!a1 ->
(#!a1)!
bar
# desugared
Task.await (foo) \#!a1 ->
Task.await #!a1 \{} -> bar
*/
let arena = &Bump::new();
let src = r#"
main =
foo!!
bar
"#;
let mut defs = parse_defs_with(arena, src).unwrap();
desugar_defs_node_values(arena, &mut defs, src, &mut None, "test.roc", true);
// TODO fix this... the value_defs is incorrect here in a harmless way because
// roc_parse::ast::Defs::split_defs_around is not completely correct
// so we have the value_def in the vec in Defs, but there is no tag pointing to it
let expected = r##"Defs { tags: [Index(2147483648)], regions: [@0-29], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "main", suffixed: 0 }, @24-29 Apply(@24-29 Var { module_name: "Task", ident: "await", suffixed: 0 }, [@24-29 Var { module_name: "", ident: "foo", suffixed: 0 }, @24-29 Closure([@24-29 Identifier { ident: "#!a0", suffixed: 0 }], @24-29 Apply(@24-29 Var { module_name: "Task", ident: "await", suffixed: 0 }, [@24-29 Var { module_name: "", ident: "#!a0", suffixed: 0 }, @24-29 Closure([@24-29 RecordDestructure([])], @46-49 Var { module_name: "", ident: "bar", suffixed: 0 })], BangSuffix))], BangSuffix))] }"##;
assert_multiline_str_eq!(format!("{:?}", &defs).as_str(), expected);
}
#[test]
fn apply_function_suffixed_sub_expr() {
/*
## Example of suffixed function within an Apply
x = (foo! "bar") "hello"
^^^^^^^^^^^^ is our suffixed function
baz x
# desugared
Task.await (foo "bar") \#!a1 ->
x = #!a1 "hello"
bar x
*/
let arena = &Bump::new();
let src = r#"
main =
x = (foo! "bar") "hello"
baz x
"#;
let mut defs = parse_defs_with(arena, src).unwrap();
desugar_defs_node_values(arena, &mut defs, src, &mut None, "test.roc", true);
// TODO fix this... the value_defs is incorrect here in a harmless way because
// roc_parse::ast::Defs::split_defs_around is not completely correct
// so we have the value_def in the vec in Defs, but there is no tag pointing to it
let expected = r##"Defs { tags: [Index(2147483648)], regions: [@0-70], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "main", suffixed: 0 }, @28-48 Apply(@28-48 Var { module_name: "Task", ident: "await", suffixed: 0 }, [Apply(@29-33 Var { module_name: "", ident: "foo", suffixed: 0 }, [@34-39 Str(PlainLine("bar"))], Space), @28-48 Closure([Identifier { ident: "#!a0", suffixed: 0 }], @28-48 Defs(Defs { tags: [Index(2147483650)], regions: [@28-48], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@24-25 Identifier { ident: "x", suffixed: 0 }, @28-48 Apply(@29-39 ParensAround(Apply(@29-33 Var { module_name: "", ident: "foo", suffixed: 1 }, [@34-39 Str(PlainLine("bar"))], Space)), [@41-48 Str(PlainLine("hello"))], Space)), Body(@24-25 Identifier { ident: "x", suffixed: 0 }, @28-48 Apply(@29-39 ParensAround(Var { module_name: "", ident: "#!a0", suffixed: 0 }), [@41-48 Str(PlainLine("hello"))], Space)), Body(@24-25 Identifier { ident: "x", suffixed: 0 }, @28-48 Apply(@29-39 ParensAround(Var { module_name: "", ident: "#!a0", suffixed: 0 }), [@41-48 Str(PlainLine("hello"))], Space))] }, @65-70 Apply(@65-68 Var { module_name: "", ident: "baz", suffixed: 0 }, [@69-70 Var { module_name: "", ident: "x", suffixed: 0 }], Space)))], BangSuffix))] }"##;
assert_multiline_str_eq!(format!("{:?}", &defs).as_str(), expected);
}
#[test]
fn multiple_def_first_suffixed() {
/*
## Example with multiple defs, first suffixed
msg = "hello"
x = foo! msg
bar x
# desugared
msg = "hello"
Task.await (foo msg) \x -> bar x
*/
let arena = &Bump::new();
let src = r#"
main =
msg = "hello"
x = foo! msg
bar x
"#;
let mut defs = parse_defs_with(arena, src).unwrap();
desugar_defs_node_values(arena, &mut defs, src, &mut None, "test.roc", true);
// TODO fix this... the value_defs is incorrect here in a harmless way because
// roc_parse::ast::Defs::split_defs_around is not completely correct
// so we have the value_def in the vec in Defs, but there is no tag pointing to it
let expected = r#"Defs { tags: [Index(2147483648)], regions: [@0-88], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "main", suffixed: 0 }, @24-88 Defs(Defs { tags: [Index(2147483650)], regions: [@24-88, @54-66], 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(@24-27 Identifier { ident: "msg", suffixed: 0 }, @30-37 Str(PlainLine("hello"))), Body(@54-55 Identifier { ident: "x", suffixed: 0 }, @58-66 Apply(@58-62 Var { module_name: "", ident: "foo", suffixed: 1 }, [@63-66 Var { module_name: "", ident: "msg", suffixed: 0 }], Space)), Body(@24-27 Identifier { ident: "msg", suffixed: 0 }, @30-37 Str(PlainLine("hello")))] }, @24-88 Apply(@24-88 Var { module_name: "Task", ident: "await", suffixed: 0 }, [@58-66 Apply(@58-66 Var { module_name: "", ident: "foo", suffixed: 0 }, [@63-66 Var { module_name: "", ident: "msg", suffixed: 0 }], Space), @24-88 Closure([@54-55 Identifier { ident: "x", suffixed: 0 }], @83-88 Apply(@83-86 Var { module_name: "", ident: "bar", suffixed: 0 }, [@87-88 Var { module_name: "", ident: "x", suffixed: 0 }], Space))], BangSuffix)))] }"#;
assert_multiline_str_eq!(format!("{:?}", &defs).as_str(), expected);
}
#[test]
fn closure_with_annotations() {
/*
x : Str -> Task _ _
x = \msg ->
y : Task {} _
y = line! msg
y
x "foo"
# desugared
x = \msg ->
Task.await (line msg) \y -> y
x "foo"
*/
let arena = &Bump::new();
let src = r#"
main =
x : Str -> Task _ _
x = \msg ->
y : Task {} _
y = line! msg
y
x "foo"
"#;
let mut defs = parse_defs_with(arena, src).unwrap();
desugar_defs_node_values(arena, &mut defs, src, &mut None, "test.roc", true);
// TODO fix this... the value_defs is incorrect here in a harmless way because
// roc_parse::ast::Defs::split_defs_around is not completely correct
// so we have the value_def in the vec in Defs, but there is no tag pointing to it
let expected = r#"Defs { tags: [Index(2147483648)], regions: [@0-187], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "main", suffixed: 0 }, @24-187 Defs(Defs { tags: [Index(2147483650)], regions: [@24-187], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Annotation(@24-25 Identifier { ident: "x", suffixed: 0 }, @28-43 Function([@28-31 Apply("", "Str", [])], @35-43 Apply("", "Task", [@40-41 Inferred, @42-43 Inferred]))), AnnotatedBody { ann_pattern: @24-25 Identifier { ident: "x", suffixed: 0 }, ann_type: @28-43 Function([@28-31 Apply("", "Str", [])], @35-43 Apply("", "Task", [@40-41 Inferred, @42-43 Inferred])), comment: None, body_pattern: @60-61 Identifier { ident: "x", suffixed: 0 }, body_expr: @64-162 Closure([@65-68 Identifier { ident: "msg", suffixed: 0 }], @93-162 Defs(Defs { tags: [Index(2147483649)], regions: [@93-140], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Annotation(@93-94 Identifier { ident: "y", suffixed: 0 }, @97-106 Apply("", "Task", [@102-104 Record { fields: [], ext: None }, @105-106 Inferred])), AnnotatedBody { ann_pattern: @93-94 Identifier { ident: "y", suffixed: 0 }, ann_type: @97-106 Apply("", "Task", [@102-104 Record { fields: [], ext: None }, @105-106 Inferred]), comment: None, body_pattern: @127-128 Identifier { ident: "y", suffixed: 0 }, body_expr: @131-140 Apply(@131-136 Var { module_name: "", ident: "line", suffixed: 1 }, [@137-140 Var { module_name: "", ident: "msg", suffixed: 0 }], Space) }] }, @161-162 Var { module_name: "", ident: "y", suffixed: 0 })) }, AnnotatedBody { ann_pattern: @24-25 Identifier { ident: "x", suffixed: 0 }, ann_type: @28-43 Function([@28-31 Apply("", "Str", [])], @35-43 Apply("", "Task", [@40-41 Inferred, @42-43 Inferred])), comment: None, body_pattern: @60-61 Identifier { ident: "x", suffixed: 0 }, body_expr: @64-162 Closure([@65-68 Identifier { ident: "msg", suffixed: 0 }], @131-140 Apply(@131-140 Var { module_name: "Task", ident: "await", suffixed: 0 }, [@131-140 Apply(@131-140 Var { module_name: "", ident: "line", suffixed: 0 }, [@137-140 Var { module_name: "", ident: "msg", suffixed: 0 }], Space), @131-140 Closure([@127-128 Identifier { ident: "y", suffixed: 0 }], @161-162 Var { module_name: "", ident: "y", suffixed: 0 })], BangSuffix)) }] }, @180-187 Apply(@180-181 Var { module_name: "", ident: "x", suffixed: 0 }, [@182-187 Str(PlainLine("foo"))], Space)))] }"#;
assert_multiline_str_eq!(format!("{:?}", &defs).as_str(), expected);
}
}

View file

@ -0,0 +1,406 @@
#[cfg(test)]
mod suffixed_tests {
use bumpalo::Bump;
use roc_can::desugar::desugar_defs_node_values;
use roc_parse::test_helpers::parse_defs_with;
use roc_test_utils::assert_multiline_str_eq;
/**
```roc
main =
line! "Ahoy"
{} = "There" |> Stdout.line!
Task.ok {}
main =
Task.await (line "Ahoy") \{} ->
Task.await ("There" |> Stdout.line) \{} ->
Task.ok {}
```
*/
#[test]
fn multi_defs_stmts() {
let arena = &Bump::new();
/*
# desugared
main =
Task.await (line "Ahoy") \{} ->
Task.await ("There" |> Stdout.line) \{} ->
Task.ok {}
*/
let src = r#"
main =
line! "Ahoy"
{} = "There" |> Stdout.line!
Task.ok {}
"#;
let mut defs = parse_defs_with(arena, src).unwrap();
desugar_defs_node_values(arena, &mut defs, src, &mut None, "test.roc", true);
let expected = r#"Defs { tags: [Index(2147483648)], regions: [@0-36], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "main", suffixed: 0 }, @0-36 Apply(@0-36 Var { module_name: "Task", ident: "await", suffixed: 0 }, [@24-36 Apply(@24-29 Var { module_name: "", ident: "line", suffixed: 0 }, [@30-36 Str(PlainLine("Ahoy"))], Space), @0-36 Closure([@24-36 RecordDestructure([])], @58-81 Apply(@58-81 Var { module_name: "Task", ident: "await", suffixed: 0 }, [@58-81 Apply(@69-81 Var { module_name: "Stdout", ident: "line", suffixed: 0 }, [@58-65 Str(PlainLine("There"))], BinOp(Pizza)), @58-81 Closure([@53-55 RecordDestructure([])], @115-125 Apply(@115-122 Var { module_name: "Task", ident: "ok", suffixed: 0 }, [@123-125 Record([])], Space))], BangSuffix))], BangSuffix))] }"#;
assert_multiline_str_eq!(format!("{:?}", &defs).as_str(), expected);
}
/**
The hello world of examples
```roc
main =
foo!
ok {}
main =
Task.await foo! \{} ->
ok {}
```
*/
#[test]
fn basic() {
let arena = &Bump::new();
let src = r#"
main =
foo!
ok {}
"#;
let mut defs = parse_defs_with(arena, src).unwrap();
desugar_defs_node_values(arena, &mut defs, src, &mut None, "test.roc", true);
let expected = r#"Defs { tags: [Index(2147483648)], regions: [@0-28], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "main", suffixed: 0 }, @24-28 Apply(@24-28 Var { module_name: "Task", ident: "await", suffixed: 0 }, [@24-28 Var { module_name: "", ident: "foo", suffixed: 0 }, @24-28 Closure([@24-28 RecordDestructure([])], @54-59 Apply(@54-56 Var { module_name: "", ident: "ok", suffixed: 0 }, [@57-59 Record([])], Space))], BangSuffix))] }"#;
dbg!(&defs);
assert_multiline_str_eq!(format!("{:?}", &defs).as_str(), expected);
}
/**
Example of unwrapping a pipline statement
```roc
main =
"hello"
|> Str.concat "world"
|> line!
Task.ok {}
main =
Task.await ("hello" |> Str.concat "world" |> line) \{} ->
Task.ok {}
```
*/
#[test]
fn simple_pizza() {
let arena = &Bump::new();
let src = r#"
main =
"hello"
|> Str.concat "world"
|> line!
Task.ok {}
"#;
let mut defs = parse_defs_with(arena, src).unwrap();
desugar_defs_node_values(arena, &mut defs, src, &mut None, "test.roc", true);
let expected = r#"Defs { tags: [Index(2147483648)], regions: [@0-94], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "main", suffixed: 0 }, @24-94 Apply(@24-94 Var { module_name: "Task", ident: "await", suffixed: 0 }, [@24-94 Apply(@89-94 Var { module_name: "", ident: "line", suffixed: 0 }, [@24-69 Apply(@51-61 Var { module_name: "Str", ident: "concat", suffixed: 0 }, [@24-31 Str(PlainLine("hello")), @62-69 Str(PlainLine("world"))], BinOp(Pizza))], BinOp(Pizza)), @24-94 Closure([@24-94 RecordDestructure([])], @120-130 Apply(@120-127 Var { module_name: "Task", ident: "ok", suffixed: 0 }, [@128-130 Record([])], Space))], BangSuffix))] }"#;
dbg!(&defs);
assert_multiline_str_eq!(format!("{:?}", &defs).as_str(), expected);
}
/**
Example with a Parens sub-expression in an Apply function
```roc
main =
do = (sayMultiple!) "hi"
do
main =
Task.await (sayMultiple) \#!a0 ->
do = (#!a0) "hi"
do
```
*/
#[test]
fn body_parens_apply() {
let arena = &Bump::new();
let src = r#"
main =
do = (sayMultiple!) "hi"
do
"#;
let mut defs = parse_defs_with(arena, src).unwrap();
desugar_defs_node_values(arena, &mut defs, src, &mut None, "test.roc", true);
let expected = r##"Defs { tags: [Index(2147483648)], regions: [@0-66], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "main", suffixed: 0 }, @23-66 Defs(Defs { tags: [Index(2147483649)], regions: [@23-66], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@23-25 Identifier { ident: "do", suffixed: 0 }, @28-47 Apply(@29-41 ParensAround(Var { module_name: "", ident: "sayMultiple", suffixed: 1 }), [@43-47 Str(PlainLine("hi"))], Space)), Body(@23-25 Identifier { ident: "do", suffixed: 0 }, @28-47 Apply(@28-47 Var { module_name: "Task", ident: "await", suffixed: 0 }, [ParensAround(Var { module_name: "", ident: "sayMultiple", suffixed: 0 }), @28-47 Closure([Identifier { ident: "#!a0", suffixed: 0 }], @28-47 Apply(Var { module_name: "", ident: "#!a0", suffixed: 0 }, [@43-47 Str(PlainLine("hi"))], Space))], BangSuffix))] }, @64-66 Var { module_name: "", ident: "do", suffixed: 0 }))] }"##;
assert_multiline_str_eq!(format!("{:?}", &defs).as_str(), expected);
}
/**
Example of handling Var's with single and multiple suffixes
```roc
main =
a = foo!
b = bar!!
baz a b
main =
Task.await foo \a ->
b = bar!!
baz a b
main =
Task.await foo \a ->
Tas.await bar \#!a0 ->
b = #!a0!
baz a b
main =
Task.await foo \a ->
Task.await bar \#!a0 ->
Task.await #!a0 \b -> baz a b
```
*/
#[test]
fn var_suffixes() {
let arena = &Bump::new();
let src = r#"
main =
a = foo!
b = bar!!
baz a b
"#;
let mut defs = parse_defs_with(arena, src).unwrap();
desugar_defs_node_values(arena, &mut defs, src, &mut None, "test.roc", true);
let expected = r##"Defs { tags: [Index(2147483648)], regions: [@0-81], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "main", suffixed: 0 }, @23-81 Apply(@23-81 Var { module_name: "Task", ident: "await", suffixed: 0 }, [@27-31 Var { module_name: "", ident: "foo", suffixed: 0 }, @23-81 Closure([@23-24 Identifier { ident: "a", suffixed: 0 }], @52-57 Apply(@52-57 Var { module_name: "Task", ident: "await", suffixed: 0 }, [@52-57 Var { module_name: "", ident: "bar", suffixed: 0 }, @52-57 Closure([@52-57 Identifier { ident: "#!a0", suffixed: 0 }], @52-57 Apply(@52-57 Var { module_name: "Task", ident: "await", suffixed: 0 }, [@52-57 Var { module_name: "", ident: "#!a0", suffixed: 0 }, @52-57 Closure([@48-49 Identifier { ident: "b", suffixed: 0 }], @74-81 Apply(@74-77 Var { module_name: "", ident: "baz", suffixed: 0 }, [@78-79 Var { module_name: "", ident: "a", suffixed: 0 }, @80-81 Var { module_name: "", ident: "b", suffixed: 0 }], Space))], BangSuffix))], BangSuffix))], BangSuffix))] }"##;
assert_multiline_str_eq!(format!("{:?}", &defs).as_str(), expected);
}
/**
Example with multiple suffixes on a var
```roc
main =
foo!!
bar
main =
Task.await (foo) \#!a1 ->
(#!a1)!
bar
main =
Task.await (foo) \#!a1 ->
Task.await #!a1 \{} -> bar
```
*/
#[test]
fn multiple_suffix() {
let arena = &Bump::new();
let src = r#"
main =
foo!!
bar
"#;
let mut defs = parse_defs_with(arena, src).unwrap();
desugar_defs_node_values(arena, &mut defs, src, &mut None, "test.roc", true);
// TODO fix this... the value_defs is incorrect here in a harmless way because
// roc_parse::ast::Defs::split_defs_around is not completely correct
// so we have the value_def in the vec in Defs, but there is no tag pointing to it
let expected = r##"Defs { tags: [Index(2147483648)], regions: [@0-29], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "main", suffixed: 0 }, @24-29 Apply(@24-29 Var { module_name: "Task", ident: "await", suffixed: 0 }, [@24-29 Var { module_name: "", ident: "foo", suffixed: 0 }, @24-29 Closure([@24-29 Identifier { ident: "#!a0", suffixed: 0 }], @24-29 Apply(@24-29 Var { module_name: "Task", ident: "await", suffixed: 0 }, [@24-29 Var { module_name: "", ident: "#!a0", suffixed: 0 }, @24-29 Closure([@24-29 RecordDestructure([])], @46-49 Var { module_name: "", ident: "bar", suffixed: 0 })], BangSuffix))], BangSuffix))] }"##;
assert_multiline_str_eq!(format!("{:?}", &defs).as_str(), expected);
}
/**
Example of nesting suffixed Apply functions
```roc
main =
x = (foo! "bar") "hello"
baz x
main =
Task.await (foo "bar") \#!a0 ->
x = #!a0 "hello"
baz x
```
*/
#[test]
fn apply_function_suffixed() {
let arena = &Bump::new();
let src = r#"
main =
x = (foo! "bar") "hello"
baz x
"#;
let mut defs = parse_defs_with(arena, src).unwrap();
desugar_defs_node_values(arena, &mut defs, src, &mut None, "test.roc", true);
// TODO fix this... the value_defs is incorrect here in a harmless way because
// roc_parse::ast::Defs::split_defs_around is not completely correct
// so we have the value_def in the vec in Defs, but there is no tag pointing to it
let expected = r##"Defs { tags: [Index(2147483648)], regions: [@0-70], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "main", suffixed: 0 }, @28-48 Apply(@28-48 Var { module_name: "Task", ident: "await", suffixed: 0 }, [Apply(@29-33 Var { module_name: "", ident: "foo", suffixed: 0 }, [@34-39 Str(PlainLine("bar"))], Space), @28-48 Closure([@29-33 Identifier { ident: "#!a0", suffixed: 0 }], @28-48 Defs(Defs { tags: [Index(2147483650)], regions: [@28-48], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@24-25 Identifier { ident: "x", suffixed: 0 }, @28-48 Apply(@29-39 ParensAround(Apply(@29-33 Var { module_name: "", ident: "foo", suffixed: 1 }, [@34-39 Str(PlainLine("bar"))], Space)), [@41-48 Str(PlainLine("hello"))], Space)), Body(@24-25 Identifier { ident: "x", suffixed: 0 }, @28-48 Apply(@29-39 ParensAround(Var { module_name: "", ident: "#!a0", suffixed: 0 }), [@41-48 Str(PlainLine("hello"))], Space)), Body(@24-25 Identifier { ident: "x", suffixed: 0 }, @28-48 Apply(@29-39 ParensAround(Var { module_name: "", ident: "#!a0", suffixed: 0 }), [@41-48 Str(PlainLine("hello"))], Space))] }, @65-70 Apply(@65-68 Var { module_name: "", ident: "baz", suffixed: 0 }, [@69-70 Var { module_name: "", ident: "x", suffixed: 0 }], Space)))], BangSuffix))] }"##;
dbg!(&defs);
assert_multiline_str_eq!(format!("{:?}", &defs).as_str(), expected);
}
/**
Example of an Applly with an argument that needs to be unwrapped
```roc
main =
x = bar (foo! "hello")
baz x
main =
Task.await foo "hello" \#!a0 ->
x = bar (#!a0)
baz x
```
*/
#[test]
fn apply_argument_suffixed() {
let arena = &Bump::new();
let src = r#"
main =
x = bar (foo! "hello")
baz x
"#;
let mut defs = parse_defs_with(arena, src).unwrap();
desugar_defs_node_values(arena, &mut defs, src, &mut None, "test.roc", true);
// TODO fix this... the value_defs is incorrect here in a harmless way because
// roc_parse::ast::Defs::split_defs_around is not completely correct
// so we have the value_def in the vec in Defs, but there is no tag pointing to it
let expected = r##"Defs { tags: [Index(2147483648)], regions: [@0-68], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "main", suffixed: 0 }, @28-46 Apply(@28-46 Var { module_name: "Task", ident: "await", suffixed: 0 }, [Apply(@33-37 Var { module_name: "", ident: "foo", suffixed: 0 }, [@38-45 Str(PlainLine("hello"))], Space), @28-46 Closure([@33-37 Identifier { ident: "#!a0", suffixed: 0 }], @28-46 Defs(Defs { tags: [Index(2147483650)], regions: [@28-46], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@24-25 Identifier { ident: "x", suffixed: 0 }, @28-46 Apply(@28-31 Var { module_name: "", ident: "bar", suffixed: 0 }, [@33-45 ParensAround(Apply(@33-37 Var { module_name: "", ident: "foo", suffixed: 1 }, [@38-45 Str(PlainLine("hello"))], Space))], Space)), Body(@24-25 Identifier { ident: "x", suffixed: 0 }, @28-46 Apply(@28-31 Var { module_name: "", ident: "bar", suffixed: 0 }, [@33-45 ParensAround(Var { module_name: "", ident: "#!a0", suffixed: 0 })], Space)), Body(@24-25 Identifier { ident: "x", suffixed: 0 }, @28-46 Apply(@28-31 Var { module_name: "", ident: "bar", suffixed: 0 }, [@33-45 ParensAround(Var { module_name: "", ident: "#!a0", suffixed: 0 })], Space))] }, @63-68 Apply(@63-66 Var { module_name: "", ident: "baz", suffixed: 0 }, [@67-68 Var { module_name: "", ident: "x", suffixed: 0 }], Space)))], BangSuffix))] }"##;
dbg!(&defs);
assert_multiline_str_eq!(format!("{:?}", &defs).as_str(), expected);
}
/**
Example with multiple defs, the suffixed is not the first def
```roc
main =
msg = "hello"
x = foo! msg
bar x
main =
msg = "hello"
Task.await (foo msg) \x -> bar x
```
*/
#[test]
fn multiple_def_first_suffixed() {
let arena = &Bump::new();
let src = r#"
main =
msg = "hello"
x = foo! msg
bar x
"#;
let mut defs = parse_defs_with(arena, src).unwrap();
desugar_defs_node_values(arena, &mut defs, src, &mut None, "test.roc", true);
// TODO fix this... the value_defs is incorrect here in a harmless way because
// roc_parse::ast::Defs::split_defs_around is not completely correct
// so we have the value_def in the vec in Defs, but there is no tag pointing to it
let expected = r#"Defs { tags: [Index(2147483648)], regions: [@0-88], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "main", suffixed: 0 }, @24-88 Defs(Defs { tags: [Index(2147483650)], regions: [@24-88, @54-66], 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(@24-27 Identifier { ident: "msg", suffixed: 0 }, @30-37 Str(PlainLine("hello"))), Body(@54-55 Identifier { ident: "x", suffixed: 0 }, @58-66 Apply(@58-62 Var { module_name: "", ident: "foo", suffixed: 1 }, [@63-66 Var { module_name: "", ident: "msg", suffixed: 0 }], Space)), Body(@24-27 Identifier { ident: "msg", suffixed: 0 }, @30-37 Str(PlainLine("hello")))] }, @24-88 Apply(@24-88 Var { module_name: "Task", ident: "await", suffixed: 0 }, [@58-66 Apply(@58-62 Var { module_name: "", ident: "foo", suffixed: 0 }, [@63-66 Var { module_name: "", ident: "msg", suffixed: 0 }], Space), @24-88 Closure([@54-55 Identifier { ident: "x", suffixed: 0 }], @83-88 Apply(@83-86 Var { module_name: "", ident: "bar", suffixed: 0 }, [@87-88 Var { module_name: "", ident: "x", suffixed: 0 }], Space))], BangSuffix)))] }"#;
assert_multiline_str_eq!(format!("{:?}", &defs).as_str(), expected);
}
/**
Example of a suffixed inside a closure, and annotated blocks
```roc
main =
x : Str -> Task _ _
x = \msg ->
y : Task {} _
y = line! msg
y
x "foo"
main =
x = \msg ->
Task.await (line msg) \y -> y
x "foo"
```
*/
#[test]
fn closure_with_annotations() {
let arena = &Bump::new();
let src = r#"
main =
x : Str -> Task _ _
x = \msg ->
y : Task {} _
y = line! msg
y
x "foo"
"#;
let mut defs = parse_defs_with(arena, src).unwrap();
desugar_defs_node_values(arena, &mut defs, src, &mut None, "test.roc", true);
// TODO fix this... the value_defs is incorrect here in a harmless way because
// roc_parse::ast::Defs::split_defs_around is not completely correct
// so we have the value_def in the vec in Defs, but there is no tag pointing to it
let expected = r#"Defs { tags: [Index(2147483648)], regions: [@0-187], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "main", suffixed: 0 }, @24-187 Defs(Defs { tags: [Index(2147483650)], regions: [@24-187], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Annotation(@24-25 Identifier { ident: "x", suffixed: 0 }, @28-43 Function([@28-31 Apply("", "Str", [])], @35-43 Apply("", "Task", [@40-41 Inferred, @42-43 Inferred]))), AnnotatedBody { ann_pattern: @24-25 Identifier { ident: "x", suffixed: 0 }, ann_type: @28-43 Function([@28-31 Apply("", "Str", [])], @35-43 Apply("", "Task", [@40-41 Inferred, @42-43 Inferred])), comment: None, body_pattern: @60-61 Identifier { ident: "x", suffixed: 0 }, body_expr: @64-162 Closure([@65-68 Identifier { ident: "msg", suffixed: 0 }], @93-162 Defs(Defs { tags: [Index(2147483649)], regions: [@93-140], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Annotation(@93-94 Identifier { ident: "y", suffixed: 0 }, @97-106 Apply("", "Task", [@102-104 Record { fields: [], ext: None }, @105-106 Inferred])), AnnotatedBody { ann_pattern: @93-94 Identifier { ident: "y", suffixed: 0 }, ann_type: @97-106 Apply("", "Task", [@102-104 Record { fields: [], ext: None }, @105-106 Inferred]), comment: None, body_pattern: @127-128 Identifier { ident: "y", suffixed: 0 }, body_expr: @131-140 Apply(@131-136 Var { module_name: "", ident: "line", suffixed: 1 }, [@137-140 Var { module_name: "", ident: "msg", suffixed: 0 }], Space) }] }, @161-162 Var { module_name: "", ident: "y", suffixed: 0 })) }, AnnotatedBody { ann_pattern: @24-25 Identifier { ident: "x", suffixed: 0 }, ann_type: @28-43 Function([@28-31 Apply("", "Str", [])], @35-43 Apply("", "Task", [@40-41 Inferred, @42-43 Inferred])), comment: None, body_pattern: @60-61 Identifier { ident: "x", suffixed: 0 }, body_expr: @64-162 Closure([@65-68 Identifier { ident: "msg", suffixed: 0 }], @131-140 Apply(@131-140 Var { module_name: "Task", ident: "await", suffixed: 0 }, [@131-140 Apply(@131-136 Var { module_name: "", ident: "line", suffixed: 0 }, [@137-140 Var { module_name: "", ident: "msg", suffixed: 0 }], Space), @131-140 Closure([@127-128 Identifier { ident: "y", suffixed: 0 }], @161-162 Var { module_name: "", ident: "y", suffixed: 0 })], BangSuffix)) }] }, @180-187 Apply(@180-181 Var { module_name: "", ident: "x", suffixed: 0 }, [@182-187 Str(PlainLine("foo"))], Space)))] }"#;
assert_multiline_str_eq!(format!("{:?}", &defs).as_str(), expected);
}
}

View file

@ -31,6 +31,8 @@ impl<'a> Formattable for Expr<'a> {
true
}
MalformedSuffixed(loc_expr) => loc_expr.is_multiline(),
// These expressions never have newlines
Float(..)
| Num(..)
@ -562,6 +564,10 @@ impl<'a> Formattable for Expr<'a> {
buf.indent(indent);
buf.push_str(str)
}
MalformedSuffixed(loc_expr) => {
buf.indent(indent);
loc_expr.format_with_options(buf, parens, newlines, indent);
}
MalformedClosure => {}
PrecedenceConflict { .. } => {}
MultipleRecordBuilders { .. } => {}

View file

@ -764,6 +764,7 @@ impl<'a> RemoveSpaces<'a> for Expr<'a> {
}
Expr::MalformedIdent(a, b) => Expr::MalformedIdent(a, remove_spaces_bad_ident(b)),
Expr::MalformedClosure => Expr::MalformedClosure,
Expr::MalformedSuffixed(a) => Expr::MalformedSuffixed(a),
Expr::PrecedenceConflict(a) => Expr::PrecedenceConflict(a),
Expr::MultipleRecordBuilders(a) => Expr::MultipleRecordBuilders(a),
Expr::UnappliedRecordBuilder(a) => Expr::UnappliedRecordBuilder(a),

View file

@ -5985,6 +5985,11 @@ fn build_pending_specializations<'a>(
// this seems to work for now
*shadowed
}
Pattern::RecordDestructure { .. } => {
// this seems to work for now, we added to enable suffixed expressions
// the issue is likely that we can have a `main = say! "hi"` which is
symbol
}
_ => todo!("top-level destrucuture patterns are not implemented"),
};

View file

@ -340,6 +340,7 @@ pub enum Expr<'a> {
// Problems
MalformedIdent(&'a str, crate::ident::BadIdent),
MalformedClosure,
MalformedSuffixed(&'a Loc<Expr<'a>>),
// Both operators were non-associative, e.g. (True == False == False).
// We should tell the author to disambiguate by grouping them with parens.
PrecedenceConflict(&'a PrecedenceConflict<'a>),
@ -748,7 +749,7 @@ impl<'a> Defs<'a> {
}
// For desugaring Suffixed Defs we need to split the defs around the Suffixed value
pub fn split_defs_around(&self, target: usize) -> SplitDefsAround {
pub fn split_defs_around(self, target: usize) -> SplitDefsAround<'a> {
let mut before = self.clone();
let mut after = self.clone();
@ -1807,6 +1808,7 @@ impl<'a> Malformed for Expr<'a> {
MalformedIdent(_, _) |
MalformedClosure |
MalformedSuffixed(..) |
PrecedenceConflict(_) |
MultipleRecordBuilders(_) |
UnappliedRecordBuilder(_) => true,

View file

@ -2089,6 +2089,7 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
| Expr::Dbg(_, _)
| Expr::LowLevelDbg(_, _, _)
| Expr::MalformedClosure
| Expr::MalformedSuffixed(..)
| Expr::PrecedenceConflict { .. }
| Expr::MultipleRecordBuilders { .. }
| Expr::UnappliedRecordBuilder { .. }

View file

@ -356,6 +356,7 @@ impl Problem {
| Problem::RuntimeError(RuntimeError::MalformedIdentifier(_, _, region))
| Problem::RuntimeError(RuntimeError::MalformedTypeName(_, region))
| Problem::RuntimeError(RuntimeError::MalformedClosure(region))
| Problem::RuntimeError(RuntimeError::MalformedSuffixed(region))
| Problem::RuntimeError(RuntimeError::InvalidRecordUpdate { region })
| Problem::RuntimeError(RuntimeError::InvalidFloat(_, region, _))
| Problem::RuntimeError(RuntimeError::InvalidInt(_, _, region, _))
@ -612,6 +613,8 @@ pub enum RuntimeError {
MultipleRecordBuilders(Region),
UnappliedRecordBuilder(Region),
MalformedSuffixed(Region),
}
impl RuntimeError {
@ -645,6 +648,7 @@ impl RuntimeError {
| RuntimeError::MalformedIdentifier(_, _, region)
| RuntimeError::MalformedTypeName(_, region)
| RuntimeError::MalformedClosure(region)
| RuntimeError::MalformedSuffixed(region)
| RuntimeError::InvalidRecordUpdate { region }
| RuntimeError::InvalidFloat(_, region, _)
| RuntimeError::InvalidInt(_, _, region, _)