mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-03 19:58:18 +00:00
Initial working version of proper try
keyword
This commit is contained in:
parent
a7168a4ad6
commit
eedade8e81
33 changed files with 1036 additions and 521 deletions
|
@ -31,6 +31,7 @@ pub struct Constraints {
|
|||
pub cycles: Vec<Cycle>,
|
||||
pub fx_call_constraints: Vec<FxCallConstraint>,
|
||||
pub fx_suffix_constraints: Vec<FxSuffixConstraint>,
|
||||
pub try_target_constraints: Vec<TryTargetConstraint>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Constraints {
|
||||
|
@ -87,6 +88,7 @@ impl Constraints {
|
|||
let cycles = Vec::new();
|
||||
let fx_call_constraints = Vec::with_capacity(16);
|
||||
let fx_suffix_constraints = Vec::new();
|
||||
let result_type_constraints = Vec::new();
|
||||
|
||||
categories.extend([
|
||||
Category::Record,
|
||||
|
@ -103,7 +105,6 @@ impl Constraints {
|
|||
Category::List,
|
||||
Category::Str,
|
||||
Category::Character,
|
||||
Category::Return,
|
||||
]);
|
||||
|
||||
pattern_categories.extend([
|
||||
|
@ -138,6 +139,7 @@ impl Constraints {
|
|||
cycles,
|
||||
fx_call_constraints,
|
||||
fx_suffix_constraints,
|
||||
try_target_constraints: result_type_constraints,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -635,6 +637,25 @@ impl Constraints {
|
|||
Constraint::FlexToPure(fx_var)
|
||||
}
|
||||
|
||||
pub fn try_target(
|
||||
&mut self,
|
||||
result_type_index: TypeOrVar,
|
||||
ok_payload_var: Variable,
|
||||
err_payload_var: Variable,
|
||||
region: Region,
|
||||
) -> Constraint {
|
||||
let constraint = TryTargetConstraint {
|
||||
target_type_index: result_type_index,
|
||||
ok_payload_var,
|
||||
err_payload_var,
|
||||
region,
|
||||
};
|
||||
|
||||
let constraint_index = index_push_new(&mut self.try_target_constraints, constraint);
|
||||
|
||||
Constraint::TryTarget(constraint_index)
|
||||
}
|
||||
|
||||
pub fn contains_save_the_environment(&self, constraint: &Constraint) -> bool {
|
||||
match constraint {
|
||||
Constraint::SaveTheEnvironment => true,
|
||||
|
@ -660,6 +681,7 @@ impl Constraints {
|
|||
| Constraint::Lookup(..)
|
||||
| Constraint::Pattern(..)
|
||||
| Constraint::ExpectEffectful(..)
|
||||
| Constraint::TryTarget(_)
|
||||
| Constraint::FxCall(_)
|
||||
| Constraint::FxSuffix(_)
|
||||
| Constraint::FlexToPure(_)
|
||||
|
@ -843,6 +865,8 @@ pub enum Constraint {
|
|||
FlexToPure(Variable),
|
||||
/// Expect statement or ignored def to be effectful
|
||||
ExpectEffectful(Variable, ExpectEffectfulReason, Region),
|
||||
/// Expect value to be some kind of Result
|
||||
TryTarget(Index<TryTargetConstraint>),
|
||||
/// Used for things that always unify, e.g. blanks and runtime errors
|
||||
True,
|
||||
SaveTheEnvironment,
|
||||
|
@ -909,6 +933,14 @@ pub struct IncludesTag {
|
|||
pub region: Region,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TryTargetConstraint {
|
||||
pub target_type_index: TypeOrVar,
|
||||
pub ok_payload_var: Variable,
|
||||
pub err_payload_var: Variable,
|
||||
pub region: Region,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Cycle {
|
||||
pub def_names: Slice<(Symbol, Region)>,
|
||||
|
@ -1000,6 +1032,9 @@ impl std::fmt::Debug for Constraint {
|
|||
Self::FlexToPure(arg0) => {
|
||||
write!(f, "FlexToPure({arg0:?})")
|
||||
}
|
||||
Self::TryTarget(arg0) => {
|
||||
write!(f, "ExpectResultType({arg0:?})")
|
||||
}
|
||||
Self::True => write!(f, "True"),
|
||||
Self::SaveTheEnvironment => write!(f, "SaveTheEnvironment"),
|
||||
Self::Let(arg0, arg1) => f.debug_tuple("Let").field(arg0).field(arg1).finish(),
|
||||
|
|
|
@ -479,7 +479,7 @@ fn deep_copy_expr_help<C: CopyEnv>(env: &mut C, copied: &mut Vec<Variable>, expr
|
|||
fx_type: sub!(*fx_type),
|
||||
early_returns: early_returns
|
||||
.iter()
|
||||
.map(|(var, region)| (sub!(*var), *region))
|
||||
.map(|(var, region, type_)| (sub!(*var), *region, *type_))
|
||||
.collect(),
|
||||
name: *name,
|
||||
captured_symbols: captured_symbols
|
||||
|
@ -718,6 +718,22 @@ fn deep_copy_expr_help<C: CopyEnv>(env: &mut C, copied: &mut Vec<Variable>, expr
|
|||
symbol: *symbol,
|
||||
},
|
||||
|
||||
Try {
|
||||
result_expr,
|
||||
result_var,
|
||||
return_var,
|
||||
ok_payload_var,
|
||||
err_payload_var,
|
||||
err_ext_var,
|
||||
} => Try {
|
||||
result_expr: Box::new(result_expr.map(|e| go_help!(e))),
|
||||
result_var: sub!(*result_var),
|
||||
return_var: sub!(*return_var),
|
||||
ok_payload_var: sub!(*ok_payload_var),
|
||||
err_payload_var: sub!(*err_payload_var),
|
||||
err_ext_var: sub!(*err_ext_var),
|
||||
},
|
||||
|
||||
RuntimeError(err) => RuntimeError(err.clone()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -452,6 +452,7 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
|
|||
),
|
||||
Dbg { .. } => todo!(),
|
||||
Expect { .. } => todo!(),
|
||||
Try { .. } => todo!(),
|
||||
Return { .. } => todo!(),
|
||||
RuntimeError(_) => todo!(),
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ fn new_op_call_expr<'a>(
|
|||
match right_without_spaces {
|
||||
Try => {
|
||||
let desugared_left = desugar_expr(env, scope, left);
|
||||
return desugar_try_expr(env, scope, desugared_left);
|
||||
return Loc::at(region, Expr::LowLevelTry(desugared_left));
|
||||
}
|
||||
Apply(&Loc { value: Try, .. }, arguments, _called_via) => {
|
||||
let try_fn = desugar_expr(env, scope, arguments.first().unwrap());
|
||||
|
@ -58,13 +58,12 @@ fn new_op_call_expr<'a>(
|
|||
.map(|a| desugar_expr(env, scope, a)),
|
||||
);
|
||||
|
||||
return desugar_try_expr(
|
||||
env,
|
||||
scope,
|
||||
env.arena.alloc(Loc::at(
|
||||
right.region,
|
||||
return Loc::at(
|
||||
region,
|
||||
Expr::LowLevelTry(env.arena.alloc(Loc::at(
|
||||
region,
|
||||
Expr::Apply(try_fn, args.into_bump_slice(), CalledVia::Try),
|
||||
)),
|
||||
))),
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
|
@ -957,13 +956,17 @@ pub fn desugar_expr<'a>(
|
|||
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);
|
||||
|
||||
env.arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
args_region,
|
||||
Expr::Apply(function, desugared_args.into_bump_slice(), CalledVia::Try),
|
||||
))
|
||||
};
|
||||
|
||||
env.arena.alloc(desugar_try_expr(env, scope, result_expr))
|
||||
env.arena
|
||||
.alloc(Loc::at(loc_expr.region, Expr::LowLevelTry(result_expr)))
|
||||
}
|
||||
Apply(loc_fn, loc_args, called_via) => {
|
||||
let mut desugared_args = Vec::with_capacity_in(loc_args.len(), env.arena);
|
||||
|
@ -1134,77 +1137,11 @@ pub fn desugar_expr<'a>(
|
|||
})
|
||||
}
|
||||
|
||||
// note this only exists after desugaring
|
||||
LowLevelDbg(_, _, _) => loc_expr,
|
||||
// note these only exist after desugaring
|
||||
LowLevelDbg(_, _, _) | LowLevelTry(_) => loc_expr,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn desugar_try_expr<'a>(
|
||||
env: &mut Env<'a>,
|
||||
scope: &mut Scope,
|
||||
result_expr: &'a Loc<Expr<'a>>,
|
||||
) -> Loc<Expr<'a>> {
|
||||
let region = result_expr.region;
|
||||
let ok_symbol = env.arena.alloc(scope.gen_unique_symbol_name().to_string());
|
||||
let err_symbol = env.arena.alloc(scope.gen_unique_symbol_name().to_string());
|
||||
|
||||
let ok_branch = env.arena.alloc(WhenBranch {
|
||||
patterns: env.arena.alloc([Loc::at(
|
||||
region,
|
||||
Pattern::Apply(
|
||||
env.arena.alloc(Loc::at(region, Pattern::Tag("Ok"))),
|
||||
env.arena
|
||||
.alloc([Loc::at(region, Pattern::Identifier { ident: ok_symbol })]),
|
||||
),
|
||||
)]),
|
||||
value: Loc::at(
|
||||
region,
|
||||
Expr::Var {
|
||||
module_name: "",
|
||||
ident: ok_symbol,
|
||||
},
|
||||
),
|
||||
guard: None,
|
||||
});
|
||||
|
||||
let err_branch = env.arena.alloc(WhenBranch {
|
||||
patterns: env.arena.alloc([Loc::at(
|
||||
region,
|
||||
Pattern::Apply(
|
||||
env.arena.alloc(Loc::at(region, Pattern::Tag("Err"))),
|
||||
env.arena
|
||||
.alloc([Loc::at(region, Pattern::Identifier { ident: err_symbol })]),
|
||||
),
|
||||
)]),
|
||||
value: Loc::at(
|
||||
region,
|
||||
Expr::Return(
|
||||
env.arena.alloc(Loc::at(
|
||||
region,
|
||||
Expr::Apply(
|
||||
env.arena.alloc(Loc::at(region, Expr::Tag("Err"))),
|
||||
&*env.arena.alloc([&*env.arena.alloc(Loc::at(
|
||||
region,
|
||||
Expr::Var {
|
||||
module_name: "",
|
||||
ident: err_symbol,
|
||||
},
|
||||
))]),
|
||||
CalledVia::Try,
|
||||
),
|
||||
)),
|
||||
None,
|
||||
),
|
||||
),
|
||||
guard: None,
|
||||
});
|
||||
|
||||
Loc::at(
|
||||
region,
|
||||
Expr::When(result_expr, &*env.arena.alloc([&*ok_branch, &*err_branch])),
|
||||
)
|
||||
}
|
||||
|
||||
fn desugar_str_segments<'a>(
|
||||
env: &mut Env<'a>,
|
||||
scope: &mut Scope,
|
||||
|
|
|
@ -26,7 +26,9 @@ use roc_problem::can::{PrecedenceProblem, Problem, RuntimeError};
|
|||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::num::SingleQuoteBound;
|
||||
use roc_types::subs::{ExhaustiveMark, IllegalCycleMark, RedundantMark, VarStore, Variable};
|
||||
use roc_types::types::{Alias, Category, IndexOrField, LambdaSet, OptAbleVar, Type};
|
||||
use roc_types::types::{
|
||||
Alias, Category, EarlyReturnKind, IndexOrField, LambdaSet, OptAbleVar, Type,
|
||||
};
|
||||
use soa::Index;
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::path::PathBuf;
|
||||
|
@ -328,6 +330,15 @@ pub enum Expr {
|
|||
symbol: Symbol,
|
||||
},
|
||||
|
||||
Try {
|
||||
result_expr: Box<Loc<Expr>>,
|
||||
result_var: Variable,
|
||||
return_var: Variable,
|
||||
ok_payload_var: Variable,
|
||||
err_payload_var: Variable,
|
||||
err_ext_var: Variable,
|
||||
},
|
||||
|
||||
Return {
|
||||
return_value: Box<Loc<Expr>>,
|
||||
return_var: Variable,
|
||||
|
@ -403,9 +414,10 @@ impl Expr {
|
|||
}
|
||||
Self::Expect { .. } => Category::Expect,
|
||||
Self::Crash { .. } => Category::Crash,
|
||||
Self::Return { .. } => Category::Return,
|
||||
Self::Return { .. } => Category::Return(EarlyReturnKind::Return),
|
||||
|
||||
Self::Dbg { .. } => Category::Expect,
|
||||
Self::Try { .. } => Category::TrySuccess,
|
||||
|
||||
// these nodes place no constraints on the expression's type
|
||||
Self::RuntimeError(..) => Category::Unknown,
|
||||
|
@ -429,7 +441,7 @@ impl Expr {
|
|||
| Self::ZeroArgumentTag { .. }
|
||||
| Self::OpaqueWrapFunction(_)
|
||||
| Self::RuntimeError(..) => false,
|
||||
Self::Return { .. } => true,
|
||||
Self::Return { .. } | Self::Try { .. } => true,
|
||||
Self::List { loc_elems, .. } => loc_elems
|
||||
.iter()
|
||||
.any(|elem| elem.value.contains_any_early_returns()),
|
||||
|
@ -545,7 +557,7 @@ pub struct ClosureData {
|
|||
pub closure_type: Variable,
|
||||
pub return_type: Variable,
|
||||
pub fx_type: Variable,
|
||||
pub early_returns: Vec<(Variable, Region)>,
|
||||
pub early_returns: Vec<(Variable, Region, EarlyReturnKind)>,
|
||||
pub name: Symbol,
|
||||
pub captured_symbols: Vec<(Symbol, Variable)>,
|
||||
pub recursive: Recursive,
|
||||
|
@ -1385,6 +1397,28 @@ pub fn canonicalize_expr<'a>(
|
|||
output,
|
||||
)
|
||||
}
|
||||
ast::Expr::LowLevelTry(loc_expr) => {
|
||||
let (loc_result_expr, output) =
|
||||
canonicalize_expr(env, var_store, scope, loc_expr.region, &loc_expr.value);
|
||||
|
||||
let return_var = var_store.fresh();
|
||||
|
||||
scope
|
||||
.early_returns
|
||||
.push((return_var, loc_expr.region, EarlyReturnKind::Try));
|
||||
|
||||
(
|
||||
Try {
|
||||
result_expr: Box::new(loc_result_expr),
|
||||
result_var: var_store.fresh(),
|
||||
return_var,
|
||||
ok_payload_var: var_store.fresh(),
|
||||
err_payload_var: var_store.fresh(),
|
||||
err_ext_var: var_store.fresh(),
|
||||
},
|
||||
output,
|
||||
)
|
||||
}
|
||||
ast::Expr::Return(return_expr, after_return) => {
|
||||
let mut output = Output::default();
|
||||
|
||||
|
@ -1409,7 +1443,9 @@ pub fn canonicalize_expr<'a>(
|
|||
|
||||
let return_var = var_store.fresh();
|
||||
|
||||
scope.early_returns.push((return_var, return_expr.region));
|
||||
scope
|
||||
.early_returns
|
||||
.push((return_var, return_expr.region, EarlyReturnKind::Return));
|
||||
|
||||
(
|
||||
Return {
|
||||
|
@ -2351,6 +2387,29 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr {
|
|||
}
|
||||
}
|
||||
|
||||
Try {
|
||||
result_expr,
|
||||
result_var,
|
||||
return_var,
|
||||
ok_payload_var,
|
||||
err_payload_var,
|
||||
err_ext_var,
|
||||
} => {
|
||||
let loc_result_expr = Loc {
|
||||
region: result_expr.region,
|
||||
value: inline_calls(var_store, result_expr.value),
|
||||
};
|
||||
|
||||
Try {
|
||||
result_expr: Box::new(loc_result_expr),
|
||||
result_var,
|
||||
return_var,
|
||||
ok_payload_var,
|
||||
err_payload_var,
|
||||
err_ext_var,
|
||||
}
|
||||
}
|
||||
|
||||
LetRec(defs, loc_expr, mark) => {
|
||||
let mut new_defs = Vec::with_capacity(defs.len());
|
||||
|
||||
|
@ -2645,6 +2704,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),
|
||||
// Newlines are disallowed inside interpolation, and these all require newlines
|
||||
ast::Expr::DbgStmt { .. }
|
||||
| ast::Expr::LowLevelDbg(_, _, _)
|
||||
|
@ -3401,7 +3461,7 @@ pub struct FunctionDef {
|
|||
pub closure_type: Variable,
|
||||
pub return_type: Variable,
|
||||
pub fx_type: Variable,
|
||||
pub early_returns: Vec<(Variable, Region)>,
|
||||
pub early_returns: Vec<(Variable, Region, EarlyReturnKind)>,
|
||||
pub captured_symbols: Vec<(Symbol, Variable)>,
|
||||
pub arguments: Vec<(Variable, AnnotatedMark, Loc<Pattern>)>,
|
||||
}
|
||||
|
@ -3543,6 +3603,9 @@ pub(crate) fn get_lookup_symbols(expr: &Expr) -> Vec<ExpectLookup> {
|
|||
// Intentionally ignore the lookups in the nested `expect` condition itself,
|
||||
// because they couldn't possibly influence the outcome of this `expect`!
|
||||
}
|
||||
Expr::Try { result_expr, .. } => {
|
||||
stack.push(&result_expr.value);
|
||||
}
|
||||
Expr::Return { return_value, .. } => {
|
||||
stack.push(&return_value.value);
|
||||
}
|
||||
|
|
|
@ -371,9 +371,10 @@ pub fn canonicalize_module_defs<'a>(
|
|||
PatternType::TopLevelDef,
|
||||
);
|
||||
|
||||
for (_early_return_var, early_return_region) in &scope.early_returns {
|
||||
for (_early_return_var, early_return_region, early_return_kind) in &scope.early_returns {
|
||||
env.problem(Problem::ReturnOutsideOfFunction {
|
||||
region: *early_return_region,
|
||||
return_kind: *early_return_kind,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -964,6 +965,14 @@ fn fix_values_captured_in_closure_expr(
|
|||
);
|
||||
}
|
||||
|
||||
Try { result_expr, .. } => {
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut result_expr.value,
|
||||
no_capture_symbols,
|
||||
closure_captures,
|
||||
);
|
||||
}
|
||||
|
||||
Return { return_value, .. } => {
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut return_value.value,
|
||||
|
|
|
@ -5,7 +5,7 @@ use roc_module::symbol::{IdentId, IdentIds, ModuleId, ModuleIds, Symbol};
|
|||
use roc_problem::can::{RuntimeError, ScopeModuleSource};
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::Variable;
|
||||
use roc_types::types::{Alias, AliasKind, AliasVar, Type};
|
||||
use roc_types::types::{Alias, AliasKind, AliasVar, EarlyReturnKind, Type};
|
||||
|
||||
use crate::abilities::PendingAbilitiesStore;
|
||||
|
||||
|
@ -49,7 +49,7 @@ pub struct Scope {
|
|||
/// We won't intern them because they're only used during canonicalization for error reporting.
|
||||
ignored_locals: VecMap<String, Region>,
|
||||
|
||||
pub early_returns: Vec<(Variable, Region)>,
|
||||
pub early_returns: Vec<(Variable, Region, EarlyReturnKind)>,
|
||||
}
|
||||
|
||||
impl Scope {
|
||||
|
|
|
@ -400,6 +400,16 @@ pub fn walk_expr<V: Visitor>(visitor: &mut V, expr: &Expr, var: Variable) {
|
|||
Variable::NULL,
|
||||
);
|
||||
}
|
||||
Expr::Try {
|
||||
result_expr,
|
||||
result_var,
|
||||
return_var: _,
|
||||
ok_payload_var: _,
|
||||
err_payload_var: _,
|
||||
err_ext_var: _,
|
||||
} => {
|
||||
visitor.visit_expr(&result_expr.value, result_expr.region, *result_var);
|
||||
}
|
||||
Expr::Return {
|
||||
return_value,
|
||||
return_var,
|
||||
|
|
|
@ -816,64 +816,13 @@ mod test_can {
|
|||
|
||||
// Assert that we desugar to:
|
||||
//
|
||||
// when Str.toU64 "123" is
|
||||
// Ok `0` -> `0`
|
||||
// Err `1` -> return Err `1`
|
||||
// Try(Str.toU64 "123")
|
||||
|
||||
let (cond_expr, branches) = assert_when(&out.loc_expr.value);
|
||||
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");
|
||||
|
||||
assert_eq!(branches.len(), 2);
|
||||
|
||||
assert_eq!(branches[0].patterns.len(), 1);
|
||||
assert!(!branches[0].patterns[0].degenerate);
|
||||
match &branches[0].patterns[0].pattern.value {
|
||||
Pattern::AppliedTag {
|
||||
tag_name,
|
||||
arguments,
|
||||
..
|
||||
} => {
|
||||
assert_eq!(tag_name.0.to_string(), "Ok");
|
||||
assert_eq!(arguments.len(), 1);
|
||||
assert_pattern_name(&arguments[0].1.value, "0", &out.interns);
|
||||
}
|
||||
other => panic!("First argument was not an applied tag: {:?}", other),
|
||||
}
|
||||
|
||||
assert!(&branches[0].guard.is_none());
|
||||
assert_var_usage(&branches[0].value.value, "0", &out.interns);
|
||||
|
||||
assert_eq!(branches[1].patterns.len(), 1);
|
||||
assert!(!branches[1].patterns[0].degenerate);
|
||||
match &branches[1].patterns[0].pattern.value {
|
||||
Pattern::AppliedTag {
|
||||
tag_name,
|
||||
arguments,
|
||||
..
|
||||
} => {
|
||||
assert_eq!(tag_name.0.to_string(), "Err");
|
||||
assert_eq!(arguments.len(), 1);
|
||||
assert_pattern_name(&arguments[0].1.value, "1", &out.interns);
|
||||
}
|
||||
other => panic!("First argument was not an applied tag: {:?}", other),
|
||||
}
|
||||
|
||||
match &branches[1].value.value {
|
||||
Expr::Return { return_value, .. } => match &return_value.value {
|
||||
Expr::Tag {
|
||||
name, arguments, ..
|
||||
} => {
|
||||
assert_eq!(name.0.to_string(), "Err");
|
||||
assert_eq!(arguments.len(), 1);
|
||||
assert_var_usage(&arguments[0].1.value, "1", &out.interns);
|
||||
}
|
||||
other_inner => panic!("Expr was not a Tag: {:?}", other_inner),
|
||||
},
|
||||
other_outer => panic!("Expr was not a Return: {:?}", other_outer),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -890,64 +839,13 @@ mod test_can {
|
|||
|
||||
// Assert that we desugar to:
|
||||
//
|
||||
// when Str.toU64 "123" is
|
||||
// Ok `0` -> `0`
|
||||
// Err `1` -> return Err `1`
|
||||
// Try(Str.toU64 "123")
|
||||
|
||||
let (cond_expr, branches) = assert_when(&out.loc_expr.value);
|
||||
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");
|
||||
|
||||
assert_eq!(branches.len(), 2);
|
||||
|
||||
assert_eq!(branches[0].patterns.len(), 1);
|
||||
assert!(!branches[0].patterns[0].degenerate);
|
||||
match &branches[0].patterns[0].pattern.value {
|
||||
Pattern::AppliedTag {
|
||||
tag_name,
|
||||
arguments,
|
||||
..
|
||||
} => {
|
||||
assert_eq!(tag_name.0.to_string(), "Ok");
|
||||
assert_eq!(arguments.len(), 1);
|
||||
assert_pattern_name(&arguments[0].1.value, "0", &out.interns);
|
||||
}
|
||||
other => panic!("First argument was not an applied tag: {:?}", other),
|
||||
}
|
||||
|
||||
assert!(&branches[0].guard.is_none());
|
||||
assert_var_usage(&branches[0].value.value, "0", &out.interns);
|
||||
|
||||
assert_eq!(branches[1].patterns.len(), 1);
|
||||
assert!(!branches[1].patterns[0].degenerate);
|
||||
match &branches[1].patterns[0].pattern.value {
|
||||
Pattern::AppliedTag {
|
||||
tag_name,
|
||||
arguments,
|
||||
..
|
||||
} => {
|
||||
assert_eq!(tag_name.0.to_string(), "Err");
|
||||
assert_eq!(arguments.len(), 1);
|
||||
assert_pattern_name(&arguments[0].1.value, "1", &out.interns);
|
||||
}
|
||||
other => panic!("First argument was not an applied tag: {:?}", other),
|
||||
}
|
||||
|
||||
match &branches[1].value.value {
|
||||
Expr::Return { return_value, .. } => match &return_value.value {
|
||||
Expr::Tag {
|
||||
name, arguments, ..
|
||||
} => {
|
||||
assert_eq!(name.0.to_string(), "Err");
|
||||
assert_eq!(arguments.len(), 1);
|
||||
assert_var_usage(&arguments[0].1.value, "1", &out.interns);
|
||||
}
|
||||
other_inner => panic!("Expr was not a Tag: {:?}", other_inner),
|
||||
},
|
||||
other_outer => panic!("Expr was not a Return: {:?}", other_outer),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -964,64 +862,13 @@ mod test_can {
|
|||
|
||||
// Assert that we desugar to:
|
||||
//
|
||||
// when Str.toU64 "123" is
|
||||
// Ok `0` -> `0`
|
||||
// Err `1` -> return Err `1`
|
||||
// Try(Str.toU64 "123")
|
||||
|
||||
let (cond_expr, branches) = assert_when(&out.loc_expr.value);
|
||||
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");
|
||||
|
||||
assert_eq!(branches.len(), 2);
|
||||
|
||||
assert_eq!(branches[0].patterns.len(), 1);
|
||||
assert!(!branches[0].patterns[0].degenerate);
|
||||
match &branches[0].patterns[0].pattern.value {
|
||||
Pattern::AppliedTag {
|
||||
tag_name,
|
||||
arguments,
|
||||
..
|
||||
} => {
|
||||
assert_eq!(tag_name.0.to_string(), "Ok");
|
||||
assert_eq!(arguments.len(), 1);
|
||||
assert_pattern_name(&arguments[0].1.value, "0", &out.interns);
|
||||
}
|
||||
other => panic!("First argument was not an applied tag: {:?}", other),
|
||||
}
|
||||
|
||||
assert!(&branches[0].guard.is_none());
|
||||
assert_var_usage(&branches[0].value.value, "0", &out.interns);
|
||||
|
||||
assert_eq!(branches[1].patterns.len(), 1);
|
||||
assert!(!branches[1].patterns[0].degenerate);
|
||||
match &branches[1].patterns[0].pattern.value {
|
||||
Pattern::AppliedTag {
|
||||
tag_name,
|
||||
arguments,
|
||||
..
|
||||
} => {
|
||||
assert_eq!(tag_name.0.to_string(), "Err");
|
||||
assert_eq!(arguments.len(), 1);
|
||||
assert_pattern_name(&arguments[0].1.value, "1", &out.interns);
|
||||
}
|
||||
other => panic!("First argument was not an applied tag: {:?}", other),
|
||||
}
|
||||
|
||||
match &branches[1].value.value {
|
||||
Expr::Return { return_value, .. } => match &return_value.value {
|
||||
Expr::Tag {
|
||||
name, arguments, ..
|
||||
} => {
|
||||
assert_eq!(name.0.to_string(), "Err");
|
||||
assert_eq!(arguments.len(), 1);
|
||||
assert_var_usage(&arguments[0].1.value, "1", &out.interns);
|
||||
}
|
||||
other_inner => panic!("Expr was not a Tag: {:?}", other_inner),
|
||||
},
|
||||
other_outer => panic!("Expr was not a Return: {:?}", other_outer),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1142,6 +989,13 @@ mod test_can {
|
|||
}
|
||||
}
|
||||
|
||||
fn assert_try_expr(expr: &Expr) -> &Expr {
|
||||
match expr {
|
||||
Expr::Try { result_expr, .. } => &result_expr.value,
|
||||
_ => panic!("Expr was not a Try: {:?}", expr),
|
||||
}
|
||||
}
|
||||
|
||||
// TAIL CALLS
|
||||
fn get_closure(expr: &Expr, i: usize) -> roc_can::expr::Recursive {
|
||||
match expr {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue