Use new try impl for ? operator

This commit is contained in:
Sam Mohr 2024-12-05 02:13:08 -08:00
parent 193c23bac8
commit de626102c8
No known key found for this signature in database
GPG key ID: EA41D161A3C1BC99
20 changed files with 429 additions and 110 deletions

View file

@ -5,6 +5,7 @@ use std::sync::Arc;
use crate::abilities::SpecializationId;
use crate::exhaustive::{ExhaustiveContext, SketchedRows};
use crate::expected::{Expected, PExpected};
use crate::expr::TryKind;
use roc_collections::soa::{index_push_new, slice_extend_new};
use roc_module::ident::{IdentSuffix, TagName};
use roc_module::symbol::{ModuleId, Symbol};
@ -643,12 +644,14 @@ impl Constraints {
ok_payload_var: Variable,
err_payload_var: Variable,
region: Region,
kind: TryKind,
) -> Constraint {
let constraint = TryTargetConstraint {
target_type_index: result_type_index,
ok_payload_var,
err_payload_var,
region,
kind,
};
let constraint_index = index_push_new(&mut self.try_target_constraints, constraint);
@ -939,6 +942,7 @@ pub struct TryTargetConstraint {
pub ok_payload_var: Variable,
pub err_payload_var: Variable,
pub region: Region,
pub kind: TryKind,
}
#[derive(Debug, Clone, Copy)]

View file

@ -725,6 +725,7 @@ fn deep_copy_expr_help<C: CopyEnv>(env: &mut C, copied: &mut Vec<Variable>, expr
ok_payload_var,
err_payload_var,
err_ext_var,
kind,
} => Try {
result_expr: Box::new(result_expr.map(|e| go_help!(e))),
result_var: sub!(*result_var),
@ -732,6 +733,7 @@ fn deep_copy_expr_help<C: CopyEnv>(env: &mut C, copied: &mut Vec<Variable>, expr
ok_payload_var: sub!(*ok_payload_var),
err_payload_var: sub!(*err_payload_var),
err_ext_var: sub!(*err_ext_var),
kind: *kind,
},
RuntimeError(err) => RuntimeError(err.clone()),

View file

@ -11,8 +11,8 @@ 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, StrLiteral,
StrSegment, TypeAnnotation, ValueDef, WhenBranch,
is_expr_suffixed, AssignedField, Collection, Defs, ModuleImportParams, Pattern, ResultTryKind,
StrLiteral, StrSegment, TryTarget, TypeAnnotation, ValueDef, WhenBranch,
};
use roc_problem::can::Problem;
use roc_region::all::{Loc, Region};
@ -44,7 +44,10 @@ fn new_op_call_expr<'a>(
match right_without_spaces {
Try => {
let desugared_left = desugar_expr(env, scope, left);
return Loc::at(region, Expr::LowLevelTry(desugared_left));
return Loc::at(
region,
Expr::LowLevelTry(desugared_left, ResultTryKind::KeywordPrefix),
);
}
Apply(&Loc { value: Try, .. }, arguments, _called_via) => {
let try_fn = desugar_expr(env, scope, arguments.first().unwrap());
@ -60,10 +63,71 @@ fn new_op_call_expr<'a>(
return Loc::at(
region,
Expr::LowLevelTry(env.arena.alloc(Loc::at(
region,
Expr::Apply(try_fn, args.into_bump_slice(), CalledVia::Try),
))),
Expr::LowLevelTry(
env.arena.alloc(Loc::at(
region,
Expr::Apply(try_fn, args.into_bump_slice(), CalledVia::Try),
)),
ResultTryKind::KeywordPrefix,
),
);
}
TrySuffix {
target: TryTarget::Result,
expr: fn_expr,
} => {
let loc_fn = env.arena.alloc(Loc::at(right.region, **fn_expr));
let function = desugar_expr(env, scope, loc_fn);
return Loc::at(
region,
LowLevelTry(
env.arena.alloc(Loc::at(
region,
Expr::Apply(
function,
env.arena.alloc([desugar_expr(env, scope, left)]),
CalledVia::Try,
),
)),
ResultTryKind::OperatorSuffix,
),
);
}
Apply(
&Loc {
value:
TrySuffix {
target: TryTarget::Result,
expr: fn_expr,
},
region: fn_region,
},
loc_args,
_called_via,
) => {
let loc_fn = env.arena.alloc(Loc::at(fn_region, *fn_expr));
let function = desugar_expr(env, scope, loc_fn);
let mut desugared_args = Vec::with_capacity_in(loc_args.len() + 1, env.arena);
desugared_args.push(desugar_expr(env, scope, left));
for loc_arg in &loc_args[..] {
desugared_args.push(desugar_expr(env, scope, loc_arg));
}
return Loc::at(
region,
LowLevelTry(
env.arena.alloc(Loc::at(
region,
Expr::Apply(
function,
desugared_args.into_bump_slice(),
CalledVia::Try,
),
)),
ResultTryKind::OperatorSuffix,
),
);
}
_ => {}
@ -456,23 +520,32 @@ pub fn desugar_expr<'a>(
env.arena.alloc(Loc { region, value })
}
// desugar the sub_expression, but leave the TrySuffix as this will
// be unwrapped later in desugar_value_def_suffixed
TrySuffix {
expr: sub_expr,
target,
} => {
let intermediate = env.arena.alloc(Loc::at(loc_expr.region, **sub_expr));
let new_sub_loc_expr = desugar_expr(env, scope, intermediate);
let new_sub_expr = env.arena.alloc(new_sub_loc_expr.value);
env.arena.alloc(Loc::at(
loc_expr.region,
TrySuffix {
expr: new_sub_expr,
target: *target,
},
))
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,
},
))
}
}
}
RecordAccess(sub_expr, paths) => {
let region = loc_expr.region;
@ -965,8 +1038,43 @@ pub fn desugar_expr<'a>(
))
};
env.arena
.alloc(Loc::at(loc_expr.region, Expr::LowLevelTry(result_expr)))
env.arena.alloc(Loc::at(
loc_expr.region,
Expr::LowLevelTry(result_expr, ResultTryKind::KeywordPrefix),
))
}
Apply(
Loc {
value:
TrySuffix {
target: TryTarget::Result,
expr: fn_expr,
},
region: fn_region,
},
loc_args,
_called_via,
) => {
let loc_fn = env.arena.alloc(Loc::at(*fn_region, **fn_expr));
let function = desugar_expr(env, scope, loc_fn);
let mut desugared_args = Vec::with_capacity_in(loc_args.len(), env.arena);
for loc_arg in &loc_args[..] {
desugared_args.push(desugar_expr(env, scope, loc_arg));
}
let args_region =
Region::span_across(&loc_args[0].region, &loc_args[loc_args.len() - 1].region);
let result_expr = env.arena.alloc(Loc::at(
args_region,
Expr::Apply(function, desugared_args.into_bump_slice(), CalledVia::Try),
));
env.arena.alloc(Loc::at(
loc_expr.region,
Expr::LowLevelTry(result_expr, ResultTryKind::OperatorSuffix),
))
}
Apply(loc_fn, loc_args, called_via) => {
let mut desugared_args = Vec::with_capacity_in(loc_args.len(), env.arena);
@ -1138,7 +1246,7 @@ pub fn desugar_expr<'a>(
}
// note these only exist after desugaring
LowLevelDbg(_, _, _) | LowLevelTry(_) => loc_expr,
LowLevelDbg(_, _, _) | LowLevelTry(_, _) => loc_expr,
}
}

View file

@ -19,7 +19,7 @@ use roc_module::called_via::CalledVia;
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
use roc_module::low_level::LowLevel;
use roc_module::symbol::{IdentId, ModuleId, Symbol};
use roc_parse::ast::{self, Defs, PrecedenceConflict, StrLiteral};
use roc_parse::ast::{self, Defs, PrecedenceConflict, ResultTryKind, StrLiteral};
use roc_parse::ident::Accessor;
use roc_parse::pattern::PatternType::*;
use roc_problem::can::{PrecedenceProblem, Problem, RuntimeError};
@ -337,6 +337,7 @@ pub enum Expr {
ok_payload_var: Variable,
err_payload_var: Variable,
err_ext_var: Variable,
kind: TryKind,
},
Return {
@ -348,6 +349,12 @@ pub enum Expr {
RuntimeError(RuntimeError),
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum TryKind {
KeywordPrefix,
OperatorSuffix,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ExpectLookup {
pub symbol: Symbol,
@ -1397,7 +1404,7 @@ pub fn canonicalize_expr<'a>(
output,
)
}
ast::Expr::LowLevelTry(loc_expr) => {
ast::Expr::LowLevelTry(loc_expr, kind) => {
let (loc_result_expr, output) =
canonicalize_expr(env, var_store, scope, loc_expr.region, &loc_expr.value);
@ -1415,6 +1422,10 @@ pub fn canonicalize_expr<'a>(
ok_payload_var: var_store.fresh(),
err_payload_var: var_store.fresh(),
err_ext_var: var_store.fresh(),
kind: match kind {
ResultTryKind::KeywordPrefix => TryKind::KeywordPrefix,
ResultTryKind::OperatorSuffix => TryKind::OperatorSuffix,
},
},
output,
)
@ -2394,6 +2405,7 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr {
ok_payload_var,
err_payload_var,
err_ext_var,
kind,
} => {
let loc_result_expr = Loc {
region: result_expr.region,
@ -2407,6 +2419,7 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr {
ok_payload_var,
err_payload_var,
err_ext_var,
kind,
}
}
@ -2704,7 +2717,7 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
| ast::Expr::MalformedIdent(_, _)
| ast::Expr::Tag(_)
| ast::Expr::OpaqueRef(_) => true,
ast::Expr::LowLevelTry(loc_expr) => is_valid_interpolation(&loc_expr.value),
ast::Expr::LowLevelTry(loc_expr, _) => is_valid_interpolation(&loc_expr.value),
// Newlines are disallowed inside interpolation, and these all require newlines
ast::Expr::DbgStmt { .. }
| ast::Expr::LowLevelDbg(_, _, _)

View file

@ -407,6 +407,7 @@ pub fn walk_expr<V: Visitor>(visitor: &mut V, expr: &Expr, var: Variable) {
ok_payload_var: _,
err_payload_var: _,
err_ext_var: _,
kind: _,
} => {
visitor.visit_expr(&result_expr.value, result_expr.region, *result_var);
}

View file

@ -143,79 +143,75 @@ Defs {
ident: "i",
},
],
@132-172 Apply(
@132-172 Var {
module_name: "Result",
ident: "try",
},
[
@132-152 Apply(
@132-152 Var {
module_name: "",
ident: "inc",
},
[
@132-133 Var {
module_name: "",
ident: "i",
@113-189 Defs(
Defs {
tags: [
EitherIndex(2147483648),
],
regions: [
@132-172,
],
space_before: [
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
],
space_after: [
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
],
spaces: [],
type_defs: [],
value_defs: [
Body(
@113-117 Identifier {
ident: "newi",
},
],
BinOp(
Pizza,
),
),
@132-172 Closure(
[
@132-152 Identifier {
ident: "#!0_arg",
},
],
@132-172 Apply(
@132-172 Var {
module_name: "Result",
ident: "try",
},
[
@132-172 LowLevelTry(
@132-172 Apply(
@132-172 Var {
@169-172 Var {
module_name: "",
ident: "inc",
},
[
@132-152 Var {
module_name: "",
ident: "#!0_arg",
},
],
BinOp(
Pizza,
),
),
@132-172 Closure(
[
@113-117 Identifier {
ident: "newi",
},
],
@182-189 Apply(
@182-184 Tag(
"Ok",
@132-152 LowLevelTry(
@132-152 Apply(
@149-152 Var {
module_name: "",
ident: "inc",
},
[
@132-133 SpaceAfter(
Var {
module_name: "",
ident: "i",
},
[
Newline,
],
),
],
Try,
),
OperatorSuffix,
),
[
@185-189 Var {
module_name: "",
ident: "newi",
},
],
Space,
),
],
Try,
),
],
QuestionSuffix,
OperatorSuffix,
),
),
],
},
@182-189 Apply(
@182-184 Tag(
"Ok",
),
],
QuestionSuffix,
[
@185-189 Var {
module_name: "",
ident: "newi",
},
],
Space,
),
),
),
),

View file

@ -802,6 +802,104 @@ mod test_can {
}
}
#[test]
fn question_suffix_simple() {
let src = indoc!(
r#"
(Str.toU64 "123")?
"#
);
let arena = Bump::new();
let out = can_expr_with(&arena, test_home(), src);
assert_eq!(out.problems, Vec::new());
// Assert that we desugar to:
//
// Try(Str.toU64 "123")
let cond_expr = assert_try_expr(&out.loc_expr.value);
let cond_args = assert_func_call(cond_expr, "toU64", CalledVia::Space, &out.interns);
assert_eq!(cond_args.len(), 1);
assert_str_value(&cond_args[0].1.value, "123");
}
#[test]
fn question_suffix_after_function() {
let src = indoc!(
r#"
Str.toU64? "123"
"#
);
let arena = Bump::new();
let out = can_expr_with(&arena, test_home(), src);
assert_eq!(out.problems, Vec::new());
// Assert that we desugar to:
//
// Try(Str.toU64 "123")
let cond_expr = assert_try_expr(&out.loc_expr.value);
let cond_args = assert_func_call(cond_expr, "toU64", CalledVia::Try, &out.interns);
assert_eq!(cond_args.len(), 1);
assert_str_value(&cond_args[0].1.value, "123");
}
#[test]
fn question_suffix_pipe() {
let src = indoc!(
r#"
"123" |> Str.toU64?
"#
);
let arena = Bump::new();
let out = can_expr_with(&arena, test_home(), src);
assert_eq!(out.problems, Vec::new());
// Assert that we desugar to:
//
// Try(Str.toU64 "123")
let cond_expr = assert_try_expr(&out.loc_expr.value);
let cond_args = assert_func_call(cond_expr, "toU64", CalledVia::Try, &out.interns);
assert_eq!(cond_args.len(), 1);
assert_str_value(&cond_args[0].1.value, "123");
}
#[test]
fn question_suffix_pipe_nested() {
let src = indoc!(
r#"
"123" |> Str.toU64? (Ok 123)?
"#
);
let arena = Bump::new();
let out = can_expr_with(&arena, test_home(), src);
assert_eq!(out.problems, Vec::new());
// Assert that we desugar to:
//
// Try(Str.toU64 "123" Try(Ok 123))
let cond_expr = assert_try_expr(&out.loc_expr.value);
let cond_args = assert_func_call(cond_expr, "toU64", CalledVia::Try, &out.interns);
assert_eq!(cond_args.len(), 1);
let ok_tag = assert_try_expr(&cond_args[0].1.value);
let tag_args = assert_tag_application(ok_tag, "Ok");
assert_eq!(tag_args.len(), 1);
assert_num_value(&tag_args[0].1.value, 123);
}
#[test]
fn try_desugar_plain_prefix() {
let src = indoc!(
@ -973,6 +1071,16 @@ mod test_can {
}
}
fn assert_tag_application(expr: &Expr, tag_name: &str) -> Vec<(Variable, Loc<Expr>)> {
match expr {
Expr::LetNonRec(_, loc_expr) => assert_tag_application(&loc_expr.value, tag_name),
Expr::Tag {
name, arguments, ..
} if *name == tag_name.into() => arguments.clone(),
_ => panic!("Expr was not a Tag named {tag_name:?}: {expr:?}",),
}
}
fn assert_pattern_name(pattern: &Pattern, name: &str, interns: &roc_module::symbol::Interns) {
match pattern {
Pattern::Identifier(sym) => assert_eq!(sym.as_str(interns), name),

View file

@ -870,6 +870,7 @@ pub fn constrain_expr(
ok_payload_var,
err_payload_var,
err_ext_var,
kind,
} => {
let result_var_index = constraints.push_variable(*result_var);
let result_expected_type = constraints.push_expected_type(ForReason(
@ -891,6 +892,7 @@ pub fn constrain_expr(
*ok_payload_var,
*err_payload_var,
result_expr.region,
*kind,
);
let return_type_index = constraints.push_variable(*return_var);

View file

@ -201,7 +201,7 @@ impl<'a> Formattable for Expr<'a> {
buf.indent(indent);
buf.push_str("try");
}
LowLevelTry(_) => unreachable!(
LowLevelTry(_, _) => unreachable!(
"LowLevelTry should only exist after desugaring, not during formatting"
),
Return(return_value, after_return) => {
@ -370,7 +370,7 @@ pub fn expr_is_multiline(me: &Expr<'_>, comments_only: bool) -> bool {
| Expr::Crash
| Expr::Dbg
| Expr::Try => false,
Expr::LowLevelTry(_) => {
Expr::LowLevelTry(_, _) => {
unreachable!("LowLevelTry should only exist after desugaring, not during formatting")
}

View file

@ -14998,11 +14998,12 @@ All branches in an `if` must have the same type!
);
test_report!(
try_with_non_result_target,
keyword_try_with_non_result_target,
indoc!(
r#"
invalidTry = \{} ->
x = try 64
nonResult = "abc"
x = try nonResult
Ok (x * 2)
@ -15014,12 +15015,41 @@ All branches in an `if` must have the same type!
This expression cannot be used as a `try` target:
5 x = try 64
^^
6 x = try nonResult
^^^^^^^^^
I expected a Result, but it actually has type:
Num *
Str
Hint: Did you forget to wrap the value with an `Ok` or an `Err` tag?
"
);
test_report!(
question_try_with_non_result_target,
indoc!(
r#"
invalidTry = \{} ->
nonResult = "abc"
x = nonResult?
Ok (x * 2)
invalidTry {}
"#
),
@r"
INVALID TRY TARGET in /code/proj/Main.roc
This expression cannot be tried with the `?` operator:
6 x = nonResult?
^^^^^^^^^^
I expected a Result, but it actually has type:
Str
Hint: Did you forget to wrap the value with an `Ok` or an `Err` tag?
"
@ -15062,7 +15092,7 @@ All branches in an `if` must have the same type!
);
test_report!(
try_prefix_in_pipe,
keyword_try_prefix_in_pipe,
indoc!(
r#"
readFile : Str -> Str
@ -15097,7 +15127,7 @@ All branches in an `if` must have the same type!
);
test_report!(
try_suffix_in_pipe,
keyword_try_suffix_in_pipe,
indoc!(
r#"
readFile : Str -> Str
@ -15132,6 +15162,41 @@ All branches in an `if` must have the same type!
"
);
test_report!(
question_try_in_pipe,
indoc!(
r#"
readFile : Str -> Str
getFileContents : Str -> Result Str _
getFileContents = \filePath ->
contents =
readFile filePath
|> Result.mapErr? ErrWrapper
contents
getFileContents "file.txt"
"#
),
@r"
TYPE MISMATCH in /code/proj/Main.roc
This 1st argument to this function has an unexpected type:
9> readFile filePath
10 |> Result.mapErr? ErrWrapper
This `readFile` call produces:
Str
But this function needs its 1st argument to be:
Result ok a
"
);
test_report!(
leftover_statement,
indoc!(

View file

@ -380,6 +380,7 @@ impl<'a> LowerParams<'a> {
ok_payload_var: _,
err_payload_var: _,
err_ext_var: _,
kind: _,
} => {
expr_stack.push(&mut result_expr.value);
}

View file

@ -105,7 +105,7 @@ pub fn remove_module_param_arguments(
| TypeError::ExpectedEffectful(_, _)
| TypeError::UnsuffixedEffectfulFunction(_, _)
| TypeError::SuffixedPureFunction(_, _)
| TypeError::InvalidTryTarget(_, _) => {}
| TypeError::InvalidTryTarget(_, _, _) => {}
}
}
}

View file

@ -5878,6 +5878,7 @@ pub fn with_hole<'a>(
ok_payload_var,
err_payload_var,
err_ext_var,
kind: _,
} => {
let ok_symbol = env.unique_symbol();
let err_symbol = env.unique_symbol();

View file

@ -415,6 +415,12 @@ pub enum TryTarget {
Result,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ResultTryKind {
KeywordPrefix,
OperatorSuffix,
}
/// A parsed expression. This uses lifetimes extensively for two reasons:
///
/// 1. It uses Bump::alloc for all allocations, which returns a reference.
@ -514,7 +520,7 @@ pub enum Expr<'a> {
/// The `try` keyword that performs early return on errors
Try,
// This form of try is a desugared Result unwrapper
LowLevelTry(&'a Loc<Expr<'a>>),
LowLevelTry(&'a Loc<Expr<'a>>, ResultTryKind),
// This form of debug is a desugared call to roc_dbg
LowLevelDbg(&'a (&'a str, &'a str), &'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
@ -702,7 +708,7 @@ pub fn is_expr_suffixed(expr: &Expr) -> bool {
}
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::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))
@ -976,7 +982,7 @@ impl<'a, 'b> RecursiveValueDefIter<'a, 'b> {
expr_stack.push(&condition.value);
expr_stack.push(&cont.value);
}
LowLevelTry(loc_expr) => {
LowLevelTry(loc_expr, _) => {
expr_stack.push(&loc_expr.value);
}
Return(return_value, after_return) => {
@ -2519,7 +2525,7 @@ impl<'a> Malformed for Expr<'a> {
DbgStmt { first, extra_args, continuation } => first.is_malformed() || extra_args.iter().any(|a| a.is_malformed()) || continuation.is_malformed(),
LowLevelDbg(_, condition, continuation) => condition.is_malformed() || continuation.is_malformed(),
Try => false,
LowLevelTry(loc_expr) => loc_expr.is_malformed(),
LowLevelTry(loc_expr, _) => loc_expr.is_malformed(),
Return(return_value, after_return) => return_value.is_malformed() || after_return.is_some_and(|ar| ar.is_malformed()),
Apply(func, args, _) => func.is_malformed() || args.iter().any(|arg| arg.is_malformed()),
BinOps(firsts, last) => firsts.iter().any(|(expr, _)| expr.is_malformed()) || last.is_malformed(),

View file

@ -2180,7 +2180,7 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
| Expr::Dbg
| Expr::DbgStmt { .. }
| Expr::LowLevelDbg(_, _, _)
| Expr::LowLevelTry(_)
| Expr::LowLevelTry(_, _)
| Expr::Return(_, _)
| Expr::MalformedSuffixed(..)
| Expr::PrecedenceConflict { .. }

View file

@ -727,7 +727,7 @@ impl<'a> Normalize<'a> for Expr<'a> {
arena.alloc(b.normalize(arena)),
),
Expr::Try => Expr::Try,
Expr::LowLevelTry(a) => Expr::LowLevelTry(arena.alloc(a.normalize(arena))),
Expr::LowLevelTry(a, kind) => Expr::LowLevelTry(arena.alloc(a.normalize(arena)), kind),
Expr::Return(a, b) => Expr::Return(
arena.alloc(a.normalize(arena)),
b.map(|loc_b| &*arena.alloc(loc_b.normalize(arena))),

View file

@ -917,6 +917,7 @@ fn solve(
ok_payload_var,
err_payload_var,
region,
kind,
} = try_target_constraint;
let target_actual = either_type_index_to_var(
@ -990,7 +991,7 @@ fn solve(
Failure(vars, actual_type, _expected_type, _bad_impls) => {
env.introduce(rank, &vars);
let problem = TypeError::InvalidTryTarget(*region, actual_type);
let problem = TypeError::InvalidTryTarget(*region, actual_type, *kind);
problems.push(problem);

View file

@ -2,6 +2,7 @@
use std::{path::PathBuf, str::Utf8Error};
use roc_can::constraint::{ExpectEffectfulReason, FxSuffixKind};
use roc_can::expr::TryKind;
use roc_can::{
constraint::FxCallKind,
expected::{Expected, PExpected},
@ -48,7 +49,7 @@ pub enum TypeError {
ExpectedEffectful(Region, ExpectEffectfulReason),
UnsuffixedEffectfulFunction(Region, FxSuffixKind),
SuffixedPureFunction(Region, FxSuffixKind),
InvalidTryTarget(Region, ErrorType),
InvalidTryTarget(Region, ErrorType, TryKind),
}
impl TypeError {
@ -78,7 +79,7 @@ impl TypeError {
TypeError::FxInTopLevel(_, _) => Warning,
TypeError::UnsuffixedEffectfulFunction(_, _) => Warning,
TypeError::SuffixedPureFunction(_, _) => Warning,
TypeError::InvalidTryTarget(_, _) => RuntimeError,
TypeError::InvalidTryTarget(_, _, _) => RuntimeError,
}
}
@ -100,7 +101,7 @@ impl TypeError {
| TypeError::ExpectedEffectful(region, _)
| TypeError::UnsuffixedEffectfulFunction(region, _)
| TypeError::SuffixedPureFunction(region, _)
| TypeError::InvalidTryTarget(region, _) => Some(*region),
| TypeError::InvalidTryTarget(region, _, _) => Some(*region),
TypeError::UnfulfilledAbility(ab, ..) => ab.region(),
TypeError::Exhaustive(e) => Some(e.region()),
TypeError::CircularDef(c) => c.first().map(|ce| ce.symbol_region),