mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 16:21:11 +00:00
Merge pull request #2971 from rtfeldman/no-mono-exhaustiveness
No more exhaustiveness checking in mono, nor mono errors
This commit is contained in:
commit
4074088a5e
30 changed files with 770 additions and 781 deletions
|
@ -1,5 +1,5 @@
|
|||
use crate::def::Def;
|
||||
use crate::expr::{self, ClosureData, Expr::*, IntValue};
|
||||
use crate::expr::{self, AnnotatedMark, ClosureData, Expr::*, IntValue};
|
||||
use crate::expr::{Expr, Field, Recursive};
|
||||
use crate::num::{FloatBound, IntBound, IntWidth, NumericBound};
|
||||
use crate::pattern::Pattern;
|
||||
|
@ -9,7 +9,7 @@ use roc_module::ident::{Lowercase, TagName};
|
|||
use roc_module::low_level::LowLevel;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::{VarStore, Variable};
|
||||
use roc_types::subs::{ExhaustiveMark, RedundantMark, VarStore, Variable};
|
||||
|
||||
macro_rules! macro_magic {
|
||||
(@single $($x:tt)*) => (());
|
||||
|
@ -2622,8 +2622,16 @@ fn list_intersperse(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
recursive: Recursive::NotRecursive,
|
||||
captured_symbols: vec![(sep_sym, sep_var)],
|
||||
arguments: vec![
|
||||
(clos_acc_var, no_region(Pattern::Identifier(clos_acc_sym))),
|
||||
(sep_var, no_region(Pattern::Identifier(clos_elem_sym))),
|
||||
(
|
||||
clos_acc_var,
|
||||
AnnotatedMark::new(var_store),
|
||||
no_region(Pattern::Identifier(clos_acc_sym)),
|
||||
),
|
||||
(
|
||||
sep_var,
|
||||
AnnotatedMark::new(var_store),
|
||||
no_region(Pattern::Identifier(clos_elem_sym)),
|
||||
),
|
||||
],
|
||||
loc_body: {
|
||||
let append_sep = RunLowLevel {
|
||||
|
@ -2708,9 +2716,14 @@ fn list_split(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
arguments: vec![
|
||||
(
|
||||
clos_start_var,
|
||||
AnnotatedMark::new(var_store),
|
||||
no_region(Pattern::Identifier(clos_start_sym)),
|
||||
),
|
||||
(clos_len_var, no_region(Pattern::Identifier(clos_len_sym))),
|
||||
(
|
||||
clos_len_var,
|
||||
AnnotatedMark::new(var_store),
|
||||
no_region(Pattern::Identifier(clos_len_sym)),
|
||||
),
|
||||
],
|
||||
loc_body: {
|
||||
Box::new(no_region(RunLowLevel {
|
||||
|
@ -2894,7 +2907,11 @@ fn list_drop_if(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
name: Symbol::LIST_DROP_IF_PREDICATE,
|
||||
recursive: Recursive::NotRecursive,
|
||||
captured_symbols: vec![(sym_predicate, t_predicate)],
|
||||
arguments: vec![(t_elem, no_region(Pattern::Identifier(Symbol::ARG_3)))],
|
||||
arguments: vec![(
|
||||
t_elem,
|
||||
AnnotatedMark::new(var_store),
|
||||
no_region(Pattern::Identifier(Symbol::ARG_3)),
|
||||
)],
|
||||
loc_body: {
|
||||
let should_drop = Call(
|
||||
Box::new((
|
||||
|
@ -3078,8 +3095,16 @@ fn list_join_map(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
recursive: Recursive::NotRecursive,
|
||||
captured_symbols: vec![(Symbol::ARG_2, before2list_after)],
|
||||
arguments: vec![
|
||||
(list_after, no_region(Pattern::Identifier(Symbol::ARG_3))),
|
||||
(before, no_region(Pattern::Identifier(Symbol::ARG_4))),
|
||||
(
|
||||
list_after,
|
||||
AnnotatedMark::new(var_store),
|
||||
no_region(Pattern::Identifier(Symbol::ARG_3)),
|
||||
),
|
||||
(
|
||||
before,
|
||||
AnnotatedMark::new(var_store),
|
||||
no_region(Pattern::Identifier(Symbol::ARG_4)),
|
||||
),
|
||||
],
|
||||
loc_body: {
|
||||
let mapper = Box::new((
|
||||
|
@ -3609,8 +3634,16 @@ fn list_sort_desc(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
recursive: Recursive::NotRecursive,
|
||||
captured_symbols: vec![],
|
||||
arguments: vec![
|
||||
(num_var, no_region(Pattern::Identifier(Symbol::ARG_2))),
|
||||
(num_var, no_region(Pattern::Identifier(Symbol::ARG_3))),
|
||||
(
|
||||
num_var,
|
||||
AnnotatedMark::new(var_store),
|
||||
no_region(Pattern::Identifier(Symbol::ARG_2)),
|
||||
),
|
||||
(
|
||||
num_var,
|
||||
AnnotatedMark::new(var_store),
|
||||
no_region(Pattern::Identifier(Symbol::ARG_3)),
|
||||
),
|
||||
],
|
||||
loc_body: {
|
||||
Box::new(no_region(RunLowLevel {
|
||||
|
@ -4084,9 +4117,21 @@ fn set_walk(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
recursive: Recursive::NotRecursive,
|
||||
captured_symbols: vec![(Symbol::ARG_3, func_var)],
|
||||
arguments: vec![
|
||||
(accum_var, no_region(Pattern::Identifier(Symbol::ARG_5))),
|
||||
(key_var, no_region(Pattern::Identifier(Symbol::ARG_6))),
|
||||
(Variable::EMPTY_RECORD, no_region(Pattern::Underscore)),
|
||||
(
|
||||
accum_var,
|
||||
AnnotatedMark::new(var_store),
|
||||
no_region(Pattern::Identifier(Symbol::ARG_5)),
|
||||
),
|
||||
(
|
||||
key_var,
|
||||
AnnotatedMark::new(var_store),
|
||||
no_region(Pattern::Identifier(Symbol::ARG_6)),
|
||||
),
|
||||
(
|
||||
Variable::EMPTY_RECORD,
|
||||
AnnotatedMark::new(var_store),
|
||||
no_region(Pattern::Underscore),
|
||||
),
|
||||
],
|
||||
loc_body: Box::new(no_region(call_func)),
|
||||
});
|
||||
|
@ -4686,6 +4731,7 @@ fn result_map(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(ok),
|
||||
guard: None,
|
||||
redundant: RedundantMark::new(var_store),
|
||||
};
|
||||
|
||||
branches.push(branch);
|
||||
|
@ -4716,6 +4762,7 @@ fn result_map(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(err),
|
||||
guard: None,
|
||||
redundant: RedundantMark::new(var_store),
|
||||
};
|
||||
|
||||
branches.push(branch);
|
||||
|
@ -4728,6 +4775,7 @@ fn result_map(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
loc_cond: Box::new(no_region(Var(Symbol::ARG_1))),
|
||||
branches,
|
||||
branches_cond_var: var_store.fresh(),
|
||||
exhaustive: ExhaustiveMark::new(var_store),
|
||||
};
|
||||
|
||||
defn(
|
||||
|
@ -4784,6 +4832,7 @@ fn result_map_err(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(ok),
|
||||
guard: None,
|
||||
redundant: RedundantMark::new(var_store),
|
||||
};
|
||||
|
||||
branches.push(branch);
|
||||
|
@ -4814,6 +4863,7 @@ fn result_map_err(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(err),
|
||||
guard: None,
|
||||
redundant: RedundantMark::new(var_store),
|
||||
};
|
||||
|
||||
branches.push(branch);
|
||||
|
@ -4826,6 +4876,7 @@ fn result_map_err(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
loc_cond: Box::new(no_region(Var(Symbol::ARG_1))),
|
||||
branches,
|
||||
branches_cond_var: var_store.fresh(),
|
||||
exhaustive: ExhaustiveMark::new(var_store),
|
||||
};
|
||||
|
||||
defn(
|
||||
|
@ -4858,6 +4909,7 @@ fn result_with_default(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(Var(Symbol::ARG_3)),
|
||||
guard: None,
|
||||
redundant: RedundantMark::new(var_store),
|
||||
};
|
||||
|
||||
branches.push(branch);
|
||||
|
@ -4878,6 +4930,7 @@ fn result_with_default(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(Var(Symbol::ARG_2)),
|
||||
guard: None,
|
||||
redundant: RedundantMark::new(var_store),
|
||||
};
|
||||
|
||||
branches.push(branch);
|
||||
|
@ -4890,6 +4943,7 @@ fn result_with_default(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
loc_cond: Box::new(no_region(Var(Symbol::ARG_1))),
|
||||
branches,
|
||||
branches_cond_var: var_store.fresh(),
|
||||
exhaustive: ExhaustiveMark::new(var_store),
|
||||
};
|
||||
|
||||
defn(
|
||||
|
@ -4929,6 +4983,7 @@ fn result_is_err(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(false_expr),
|
||||
guard: None,
|
||||
redundant: RedundantMark::new(var_store),
|
||||
};
|
||||
|
||||
branches.push(branch);
|
||||
|
@ -4956,6 +5011,7 @@ fn result_is_err(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(true_expr),
|
||||
guard: None,
|
||||
redundant: RedundantMark::new(var_store),
|
||||
};
|
||||
|
||||
branches.push(branch);
|
||||
|
@ -4968,6 +5024,7 @@ fn result_is_err(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
loc_cond: Box::new(no_region(Var(Symbol::ARG_1))),
|
||||
branches,
|
||||
branches_cond_var: var_store.fresh(),
|
||||
exhaustive: ExhaustiveMark::new(var_store),
|
||||
};
|
||||
|
||||
defn(
|
||||
|
@ -5007,6 +5064,7 @@ fn result_is_ok(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(true_expr),
|
||||
guard: None,
|
||||
redundant: RedundantMark::new(var_store),
|
||||
};
|
||||
|
||||
branches.push(branch);
|
||||
|
@ -5034,6 +5092,7 @@ fn result_is_ok(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(false_expr),
|
||||
guard: None,
|
||||
redundant: RedundantMark::new(var_store),
|
||||
};
|
||||
|
||||
branches.push(branch);
|
||||
|
@ -5046,6 +5105,7 @@ fn result_is_ok(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
loc_cond: Box::new(no_region(Var(Symbol::ARG_1))),
|
||||
branches,
|
||||
branches_cond_var: var_store.fresh(),
|
||||
exhaustive: ExhaustiveMark::new(var_store),
|
||||
};
|
||||
|
||||
defn(
|
||||
|
@ -5097,6 +5157,7 @@ fn result_after(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(ok),
|
||||
guard: None,
|
||||
redundant: RedundantMark::new(var_store),
|
||||
};
|
||||
|
||||
branches.push(branch);
|
||||
|
@ -5127,6 +5188,7 @@ fn result_after(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
patterns: vec![no_region(pattern)],
|
||||
value: no_region(err),
|
||||
guard: None,
|
||||
redundant: RedundantMark::new(var_store),
|
||||
};
|
||||
|
||||
branches.push(branch);
|
||||
|
@ -5139,6 +5201,7 @@ fn result_after(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
loc_cond: Box::new(no_region(Var(Symbol::ARG_1))),
|
||||
branches,
|
||||
branches_cond_var: var_store.fresh(),
|
||||
exhaustive: ExhaustiveMark::new(var_store),
|
||||
};
|
||||
|
||||
defn(
|
||||
|
@ -5324,7 +5387,13 @@ fn defn_help(
|
|||
|
||||
let closure_args = args
|
||||
.into_iter()
|
||||
.map(|(var, symbol)| (var, no_region(Identifier(symbol))))
|
||||
.map(|(var, symbol)| {
|
||||
(
|
||||
var,
|
||||
AnnotatedMark::new(var_store),
|
||||
no_region(Identifier(symbol)),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
Closure(ClosureData {
|
||||
|
|
|
@ -4,7 +4,7 @@ use roc_collections::soa::{EitherIndex, Index, Slice};
|
|||
use roc_module::ident::TagName;
|
||||
use roc_module::symbol::{ModuleId, Symbol};
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::Variable;
|
||||
use roc_types::subs::{ExhaustiveMark, Variable};
|
||||
use roc_types::types::{Category, PatternCategory, Type};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -22,6 +22,7 @@ pub struct Constraints {
|
|||
pub strings: Vec<&'static str>,
|
||||
pub sketched_rows: Vec<SketchedRows>,
|
||||
pub eq: Vec<Eq>,
|
||||
pub pattern_eq: Vec<PatternEq>,
|
||||
}
|
||||
|
||||
impl Default for Constraints {
|
||||
|
@ -45,6 +46,7 @@ impl Constraints {
|
|||
let strings = Vec::new();
|
||||
let sketched_rows = Vec::new();
|
||||
let eq = Vec::new();
|
||||
let pattern_eq = Vec::new();
|
||||
|
||||
types.extend([
|
||||
Type::EmptyRec,
|
||||
|
@ -97,6 +99,7 @@ impl Constraints {
|
|||
strings,
|
||||
sketched_rows,
|
||||
eq,
|
||||
pattern_eq,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -610,19 +613,35 @@ impl Constraints {
|
|||
&mut self,
|
||||
real_var: Variable,
|
||||
real_region: Region,
|
||||
real_category: Category,
|
||||
expected_branches: Expected<Type>,
|
||||
category_and_expectation: Result<
|
||||
(Category, Expected<Type>),
|
||||
(PatternCategory, PExpected<Type>),
|
||||
>,
|
||||
sketched_rows: SketchedRows,
|
||||
context: ExhaustiveContext,
|
||||
exhaustive: ExhaustiveMark,
|
||||
) -> Constraint {
|
||||
let real_var = Self::push_type_variable(real_var);
|
||||
let real_category = Index::push_new(&mut self.categories, real_category);
|
||||
let expected_branches = Index::push_new(&mut self.expectations, expected_branches);
|
||||
let equality = Eq(real_var, expected_branches, real_category, real_region);
|
||||
let equality = Index::push_new(&mut self.eq, equality);
|
||||
let sketched_rows = Index::push_new(&mut self.sketched_rows, sketched_rows);
|
||||
|
||||
Constraint::Exhaustive(equality, sketched_rows, context)
|
||||
let equality = match category_and_expectation {
|
||||
Ok((category, expected)) => {
|
||||
let category = Index::push_new(&mut self.categories, category);
|
||||
let expected = Index::push_new(&mut self.expectations, expected);
|
||||
let equality = Eq(real_var, expected, category, real_region);
|
||||
let equality = Index::push_new(&mut self.eq, equality);
|
||||
Ok(equality)
|
||||
}
|
||||
Err((category, expected)) => {
|
||||
let category = Index::push_new(&mut self.pattern_categories, category);
|
||||
let expected = Index::push_new(&mut self.pattern_expectations, expected);
|
||||
let equality = PatternEq(real_var, expected, category, real_region);
|
||||
let equality = Index::push_new(&mut self.pattern_eq, equality);
|
||||
Err(equality)
|
||||
}
|
||||
};
|
||||
|
||||
Constraint::Exhaustive(equality, sketched_rows, context, exhaustive)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -637,6 +656,14 @@ pub struct Eq(
|
|||
pub Region,
|
||||
);
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct PatternEq(
|
||||
pub EitherIndex<Type, Variable>,
|
||||
pub Index<PExpected<Type>>,
|
||||
pub Index<PatternCategory>,
|
||||
pub Region,
|
||||
);
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Constraint {
|
||||
Eq(Eq),
|
||||
|
@ -672,7 +699,12 @@ pub enum Constraint {
|
|||
Index<PatternCategory>,
|
||||
Region,
|
||||
),
|
||||
Exhaustive(Index<Eq>, Index<SketchedRows>, ExhaustiveContext),
|
||||
Exhaustive(
|
||||
Result<Index<Eq>, Index<PatternEq>>,
|
||||
Index<SketchedRows>,
|
||||
ExhaustiveContext,
|
||||
ExhaustiveMark,
|
||||
),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
|
@ -727,8 +759,12 @@ impl std::fmt::Debug for Constraint {
|
|||
arg0, arg1, arg2, arg3
|
||||
)
|
||||
}
|
||||
Self::Exhaustive(arg0, arg1, arg2) => {
|
||||
write!(f, "Exhaustive({:?}, {:?}, {:?})", arg0, arg1, arg2)
|
||||
Self::Exhaustive(arg0, arg1, arg2, arg3) => {
|
||||
write!(
|
||||
f,
|
||||
"Exhaustive({:?}, {:?}, {:?}, {:?})",
|
||||
arg0, arg1, arg2, arg3
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ use crate::abilities::MemberVariables;
|
|||
use crate::annotation::canonicalize_annotation;
|
||||
use crate::annotation::IntroducedVariables;
|
||||
use crate::env::Env;
|
||||
use crate::expr::AnnotatedMark;
|
||||
use crate::expr::ClosureData;
|
||||
use crate::expr::Expr::{self, *};
|
||||
use crate::expr::{canonicalize_expr, Output, Recursive};
|
||||
|
@ -1085,7 +1086,11 @@ fn canonicalize_pending_value_def<'a>(
|
|||
region: Region::zero(),
|
||||
};
|
||||
|
||||
underscores.push((var_store.fresh(), underscore));
|
||||
underscores.push((
|
||||
var_store.fresh(),
|
||||
AnnotatedMark::known_exhaustive(),
|
||||
underscore,
|
||||
));
|
||||
}
|
||||
|
||||
let body_expr = Loc {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::annotation::IntroducedVariables;
|
||||
use crate::def::{Declaration, Def};
|
||||
use crate::expr::{ClosureData, Expr, Recursive};
|
||||
use crate::expr::{AnnotatedMark, ClosureData, Expr, Recursive};
|
||||
use crate::pattern::Pattern;
|
||||
use crate::scope::Scope;
|
||||
use roc_collections::{SendMap, VecSet};
|
||||
|
@ -8,7 +8,7 @@ use roc_module::called_via::CalledVia;
|
|||
use roc_module::ident::{Lowercase, TagName};
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::{VarStore, Variable};
|
||||
use roc_types::subs::{ExhaustiveMark, RedundantMark, VarStore, Variable};
|
||||
use roc_types::types::{AliasKind, LambdaSet, Type, TypeExtension};
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
|
@ -120,6 +120,7 @@ fn build_effect_always(
|
|||
let const_closure = {
|
||||
let arguments = vec![(
|
||||
var_store.fresh(),
|
||||
AnnotatedMark::new(var_store),
|
||||
Loc::at_zero(empty_record_pattern(var_store)),
|
||||
)];
|
||||
|
||||
|
@ -154,6 +155,7 @@ fn build_effect_always(
|
|||
|
||||
let arguments = vec![(
|
||||
var_store.fresh(),
|
||||
AnnotatedMark::new(var_store),
|
||||
Loc::at_zero(Pattern::Identifier(value_symbol)),
|
||||
)];
|
||||
|
||||
|
@ -277,6 +279,7 @@ fn build_effect_map(
|
|||
let inner_closure = {
|
||||
let arguments = vec![(
|
||||
var_store.fresh(),
|
||||
AnnotatedMark::new(var_store),
|
||||
Loc::at_zero(empty_record_pattern(var_store)),
|
||||
)];
|
||||
|
||||
|
@ -302,6 +305,7 @@ fn build_effect_map(
|
|||
let arguments = vec![
|
||||
(
|
||||
var_store.fresh(),
|
||||
AnnotatedMark::new(var_store),
|
||||
Loc::at_zero(Pattern::UnwrappedOpaque {
|
||||
opaque: effect_symbol,
|
||||
whole_var: var_store.fresh(),
|
||||
|
@ -316,6 +320,7 @@ fn build_effect_map(
|
|||
),
|
||||
(
|
||||
var_store.fresh(),
|
||||
AnnotatedMark::new(var_store),
|
||||
Loc::at_zero(Pattern::Identifier(mapper_symbol)),
|
||||
),
|
||||
];
|
||||
|
@ -466,6 +471,7 @@ fn build_effect_after(
|
|||
let arguments = vec![
|
||||
(
|
||||
var_store.fresh(),
|
||||
AnnotatedMark::new(var_store),
|
||||
Loc::at_zero(Pattern::UnwrappedOpaque {
|
||||
opaque: effect_symbol,
|
||||
whole_var: var_store.fresh(),
|
||||
|
@ -480,6 +486,7 @@ fn build_effect_after(
|
|||
),
|
||||
(
|
||||
var_store.fresh(),
|
||||
AnnotatedMark::new(var_store),
|
||||
Loc::at_zero(Pattern::Identifier(to_effect_symbol)),
|
||||
),
|
||||
];
|
||||
|
@ -579,6 +586,7 @@ fn wrap_in_effect_thunk(
|
|||
let const_closure = {
|
||||
let arguments = vec![(
|
||||
var_store.fresh(),
|
||||
AnnotatedMark::new(var_store),
|
||||
Loc::at_zero(empty_record_pattern(var_store)),
|
||||
)];
|
||||
|
||||
|
@ -720,7 +728,11 @@ fn build_effect_forever(
|
|||
|
||||
let body = build_effect_forever_body(scope, effect_symbol, forever_symbol, effect, var_store);
|
||||
|
||||
let arguments = vec![(var_store.fresh(), Loc::at_zero(Pattern::Identifier(effect)))];
|
||||
let arguments = vec![(
|
||||
var_store.fresh(),
|
||||
AnnotatedMark::new(var_store),
|
||||
Loc::at_zero(Pattern::Identifier(effect)),
|
||||
)];
|
||||
|
||||
let function_var = var_store.fresh();
|
||||
let after_closure = Expr::Closure(ClosureData {
|
||||
|
@ -939,10 +951,12 @@ fn build_effect_loop(
|
|||
let arguments = vec![
|
||||
(
|
||||
var_store.fresh(),
|
||||
AnnotatedMark::new(var_store),
|
||||
Loc::at_zero(Pattern::Identifier(state_symbol)),
|
||||
),
|
||||
(
|
||||
var_store.fresh(),
|
||||
AnnotatedMark::new(var_store),
|
||||
Loc::at_zero(Pattern::Identifier(step_symbol)),
|
||||
),
|
||||
];
|
||||
|
@ -1209,6 +1223,7 @@ fn build_effect_loop_inner_body(
|
|||
patterns: vec![Loc::at_zero(step_pattern)],
|
||||
value: Loc::at_zero(force_thunk2),
|
||||
guard: None,
|
||||
redundant: RedundantMark::new(var_store),
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1220,6 +1235,7 @@ fn build_effect_loop_inner_body(
|
|||
patterns: vec![Loc::at_zero(done_pattern)],
|
||||
value: Loc::at_zero(Expr::Var(done_symbol)),
|
||||
guard: None,
|
||||
redundant: RedundantMark::new(var_store),
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1232,6 +1248,7 @@ fn build_effect_loop_inner_body(
|
|||
loc_cond: Box::new(force_thunk_call),
|
||||
branches,
|
||||
branches_cond_var: var_store.fresh(),
|
||||
exhaustive: ExhaustiveMark::new(var_store),
|
||||
};
|
||||
|
||||
Expr::LetNonRec(
|
||||
|
@ -1254,7 +1271,7 @@ pub fn build_host_exposed_def(
|
|||
let mut pattern_vars = SendMap::default();
|
||||
pattern_vars.insert(symbol, expr_var);
|
||||
|
||||
let mut arguments: Vec<(Variable, Loc<Pattern>)> = Vec::new();
|
||||
let mut arguments: Vec<(Variable, AnnotatedMark, Loc<Pattern>)> = Vec::new();
|
||||
let mut linked_symbol_arguments: Vec<(Variable, Expr)> = Vec::new();
|
||||
let mut captured_symbols: Vec<(Symbol, Variable)> = Vec::new();
|
||||
|
||||
|
@ -1278,7 +1295,11 @@ pub fn build_host_exposed_def(
|
|||
|
||||
let arg_var = var_store.fresh();
|
||||
|
||||
arguments.push((arg_var, Loc::at_zero(Pattern::Identifier(arg_symbol))));
|
||||
arguments.push((
|
||||
arg_var,
|
||||
AnnotatedMark::new(var_store),
|
||||
Loc::at_zero(Pattern::Identifier(arg_symbol)),
|
||||
));
|
||||
|
||||
captured_symbols.push((arg_symbol, arg_var));
|
||||
linked_symbol_arguments.push((arg_var, Expr::Var(arg_symbol)));
|
||||
|
@ -1308,6 +1329,7 @@ pub fn build_host_exposed_def(
|
|||
recursive: Recursive::NotRecursive,
|
||||
arguments: vec![(
|
||||
var_store.fresh(),
|
||||
AnnotatedMark::new(var_store),
|
||||
Loc::at_zero(empty_record_pattern(var_store)),
|
||||
)],
|
||||
loc_body: Box::new(Loc::at_zero(low_level_call)),
|
||||
|
@ -1367,7 +1389,11 @@ pub fn build_host_exposed_def(
|
|||
name: effect_closure_symbol,
|
||||
captured_symbols,
|
||||
recursive: Recursive::NotRecursive,
|
||||
arguments: vec![(var_store.fresh(), Loc::at_zero(empty_record_pattern))],
|
||||
arguments: vec![(
|
||||
var_store.fresh(),
|
||||
AnnotatedMark::new(var_store),
|
||||
Loc::at_zero(empty_record_pattern),
|
||||
)],
|
||||
loc_body: Box::new(Loc::at_zero(low_level_call)),
|
||||
});
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ use roc_exhaustive::{
|
|||
};
|
||||
use roc_module::ident::{TagIdIntType, TagName};
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::{Content, FlatType, Subs, SubsFmtContent, Variable};
|
||||
use roc_types::subs::{Content, FlatType, RedundantMark, Subs, SubsFmtContent, Variable};
|
||||
use roc_types::types::AliasKind;
|
||||
|
||||
pub use roc_exhaustive::Context as ExhaustiveContext;
|
||||
|
@ -15,17 +15,40 @@ pub use roc_exhaustive::Context as ExhaustiveContext;
|
|||
pub const GUARD_CTOR: &str = "#Guard";
|
||||
pub const NONEXHAUSIVE_CTOR: &str = "#Open";
|
||||
|
||||
pub struct ExhaustiveSummary {
|
||||
pub errors: Vec<Error>,
|
||||
pub exhaustive: bool,
|
||||
pub redundancies: Vec<RedundantMark>,
|
||||
}
|
||||
|
||||
pub fn check(
|
||||
subs: &Subs,
|
||||
sketched_rows: SketchedRows,
|
||||
context: ExhaustiveContext,
|
||||
) -> Result<(), Vec<Error>> {
|
||||
) -> ExhaustiveSummary {
|
||||
let overall_region = sketched_rows.overall_region;
|
||||
// TODO: can we keep going even if we had redundant rows?
|
||||
let matrix = sketched_rows
|
||||
.reify_to_non_redundant(subs)
|
||||
.map_err(|e| vec![e])?;
|
||||
roc_exhaustive::check(overall_region, context, matrix)
|
||||
let mut all_errors = Vec::with_capacity(1);
|
||||
|
||||
let NonRedundantSummary {
|
||||
non_redundant_rows,
|
||||
errors,
|
||||
redundancies,
|
||||
} = sketched_rows.reify_to_non_redundant(subs);
|
||||
all_errors.extend(errors);
|
||||
|
||||
let exhaustive = match roc_exhaustive::check(overall_region, context, non_redundant_rows) {
|
||||
Ok(()) => true,
|
||||
Err(errors) => {
|
||||
all_errors.extend(errors);
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
ExhaustiveSummary {
|
||||
errors: all_errors,
|
||||
exhaustive,
|
||||
redundancies,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
|
@ -63,6 +86,7 @@ struct SketchedRow {
|
|||
patterns: Vec<SketchedPattern>,
|
||||
region: Region,
|
||||
guard: Guard,
|
||||
redundant_mark: RedundantMark,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
|
@ -72,7 +96,7 @@ pub struct SketchedRows {
|
|||
}
|
||||
|
||||
impl SketchedRows {
|
||||
pub fn reify_to_non_redundant(self, subs: &Subs) -> Result<Vec<Vec<Pattern>>, Error> {
|
||||
fn reify_to_non_redundant(self, subs: &Subs) -> NonRedundantSummary {
|
||||
to_nonredundant_rows(subs, self)
|
||||
}
|
||||
}
|
||||
|
@ -173,7 +197,11 @@ fn sketch_pattern(var: Variable, pattern: &crate::pattern::Pattern) -> SketchedP
|
|||
}
|
||||
}
|
||||
|
||||
pub fn sketch_rows(target_var: Variable, region: Region, patterns: &[WhenBranch]) -> SketchedRows {
|
||||
pub fn sketch_when_branches(
|
||||
target_var: Variable,
|
||||
region: Region,
|
||||
patterns: &[WhenBranch],
|
||||
) -> SketchedRows {
|
||||
let mut rows: Vec<SketchedRow> = Vec::with_capacity(patterns.len());
|
||||
|
||||
// If any of the branches has a guard, e.g.
|
||||
|
@ -197,6 +225,7 @@ pub fn sketch_rows(target_var: Variable, region: Region, patterns: &[WhenBranch]
|
|||
patterns,
|
||||
guard,
|
||||
value: _,
|
||||
redundant,
|
||||
} in patterns
|
||||
{
|
||||
let guard = if guard.is_some() {
|
||||
|
@ -242,6 +271,7 @@ pub fn sketch_rows(target_var: Variable, region: Region, patterns: &[WhenBranch]
|
|||
patterns,
|
||||
region: loc_pat.region,
|
||||
guard,
|
||||
redundant_mark: *redundant,
|
||||
};
|
||||
rows.push(row);
|
||||
}
|
||||
|
@ -253,20 +283,48 @@ pub fn sketch_rows(target_var: Variable, region: Region, patterns: &[WhenBranch]
|
|||
}
|
||||
}
|
||||
|
||||
pub fn sketch_pattern_to_rows(
|
||||
target_var: Variable,
|
||||
region: Region,
|
||||
pattern: &crate::pattern::Pattern,
|
||||
) -> SketchedRows {
|
||||
let row = SketchedRow {
|
||||
patterns: vec![sketch_pattern(target_var, pattern)],
|
||||
region,
|
||||
// A single row cannot be redundant!
|
||||
redundant_mark: RedundantMark::known_non_redundant(),
|
||||
guard: Guard::NoGuard,
|
||||
};
|
||||
SketchedRows {
|
||||
rows: vec![row],
|
||||
overall_region: region,
|
||||
}
|
||||
}
|
||||
|
||||
/// REDUNDANT PATTERNS
|
||||
|
||||
struct NonRedundantSummary {
|
||||
non_redundant_rows: Vec<Vec<Pattern>>,
|
||||
redundancies: Vec<RedundantMark>,
|
||||
errors: Vec<Error>,
|
||||
}
|
||||
|
||||
/// INVARIANT: Produces a list of rows where (forall row. length row == 1)
|
||||
fn to_nonredundant_rows(subs: &Subs, rows: SketchedRows) -> Result<Vec<Vec<Pattern>>, Error> {
|
||||
fn to_nonredundant_rows(subs: &Subs, rows: SketchedRows) -> NonRedundantSummary {
|
||||
let SketchedRows {
|
||||
rows,
|
||||
overall_region,
|
||||
} = rows;
|
||||
let mut checked_rows = Vec::with_capacity(rows.len());
|
||||
|
||||
let mut redundancies = vec![];
|
||||
let mut errors = vec![];
|
||||
|
||||
for SketchedRow {
|
||||
patterns,
|
||||
guard,
|
||||
region,
|
||||
redundant_mark,
|
||||
} in rows.into_iter()
|
||||
{
|
||||
let next_row: Vec<Pattern> = patterns
|
||||
|
@ -277,7 +335,8 @@ fn to_nonredundant_rows(subs: &Subs, rows: SketchedRows) -> Result<Vec<Vec<Patte
|
|||
if matches!(guard, Guard::HasGuard) || is_useful(checked_rows.clone(), next_row.clone()) {
|
||||
checked_rows.push(next_row);
|
||||
} else {
|
||||
return Err(Error::Redundant {
|
||||
redundancies.push(redundant_mark);
|
||||
errors.push(Error::Redundant {
|
||||
overall_region,
|
||||
branch_region: region,
|
||||
index: HumanIndex::zero_based(checked_rows.len()),
|
||||
|
@ -285,7 +344,11 @@ fn to_nonredundant_rows(subs: &Subs, rows: SketchedRows) -> Result<Vec<Vec<Patte
|
|||
}
|
||||
}
|
||||
|
||||
Ok(checked_rows)
|
||||
NonRedundantSummary {
|
||||
non_redundant_rows: checked_rows,
|
||||
redundancies,
|
||||
errors,
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_tag(subs: &Subs, whole_var: Variable, this_tag: &TagName) -> (Union, TagId) {
|
||||
|
|
|
@ -18,7 +18,7 @@ use roc_parse::ast::{self, EscapedChar, StrLiteral};
|
|||
use roc_parse::pattern::PatternType::*;
|
||||
use roc_problem::can::{PrecedenceProblem, Problem, RuntimeError};
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::{VarStore, Variable};
|
||||
use roc_types::subs::{ExhaustiveMark, RedundantMark, VarStore, Variable};
|
||||
use roc_types::types::{Alias, Category, LambdaSet, Type};
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::{char, u32};
|
||||
|
@ -84,12 +84,18 @@ pub enum Expr {
|
|||
Var(Symbol),
|
||||
// Branching
|
||||
When {
|
||||
/// The actual condition of the when expression.
|
||||
loc_cond: Box<Loc<Expr>>,
|
||||
cond_var: Variable,
|
||||
/// Result type produced by the branches.
|
||||
expr_var: Variable,
|
||||
region: Region,
|
||||
loc_cond: Box<Loc<Expr>>,
|
||||
/// The branches of the when, and the type of the condition that they expect to be matched
|
||||
/// against.
|
||||
branches: Vec<WhenBranch>,
|
||||
branches_cond_var: Variable,
|
||||
/// Whether the branches are exhaustive.
|
||||
exhaustive: ExhaustiveMark,
|
||||
},
|
||||
If {
|
||||
cond_var: Variable,
|
||||
|
@ -235,6 +241,32 @@ impl Expr {
|
|||
}
|
||||
}
|
||||
|
||||
/// Stores exhaustiveness-checking metadata for a closure argument that may
|
||||
/// have an annotated type.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct AnnotatedMark {
|
||||
pub annotation_var: Variable,
|
||||
pub exhaustive: ExhaustiveMark,
|
||||
}
|
||||
|
||||
impl AnnotatedMark {
|
||||
pub fn new(var_store: &mut VarStore) -> Self {
|
||||
Self {
|
||||
annotation_var: var_store.fresh(),
|
||||
exhaustive: ExhaustiveMark::new(var_store),
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: only ever use this if you *know* a pattern match is surely exhaustive!
|
||||
// Otherwise you will get unpleasant unification errors.
|
||||
pub fn known_exhaustive() -> Self {
|
||||
Self {
|
||||
annotation_var: Variable::EMPTY_TAG_UNION,
|
||||
exhaustive: ExhaustiveMark::known_exhaustive(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ClosureData {
|
||||
pub function_type: Variable,
|
||||
|
@ -244,7 +276,7 @@ pub struct ClosureData {
|
|||
pub name: Symbol,
|
||||
pub captured_symbols: Vec<(Symbol, Variable)>,
|
||||
pub recursive: Recursive,
|
||||
pub arguments: Vec<(Variable, Loc<Pattern>)>,
|
||||
pub arguments: Vec<(Variable, AnnotatedMark, Loc<Pattern>)>,
|
||||
pub loc_body: Box<Loc<Expr>>,
|
||||
}
|
||||
|
||||
|
@ -296,7 +328,11 @@ impl AccessorData {
|
|||
|
||||
let loc_body = Loc::at_zero(body);
|
||||
|
||||
let arguments = vec![(record_var, Loc::at_zero(Pattern::Identifier(record_symbol)))];
|
||||
let arguments = vec![(
|
||||
record_var,
|
||||
AnnotatedMark::known_exhaustive(),
|
||||
Loc::at_zero(Pattern::Identifier(record_symbol)),
|
||||
)];
|
||||
|
||||
ClosureData {
|
||||
function_type: function_var,
|
||||
|
@ -332,6 +368,8 @@ pub struct WhenBranch {
|
|||
pub patterns: Vec<Loc<Pattern>>,
|
||||
pub value: Loc<Expr>,
|
||||
pub guard: Option<Loc<Expr>>,
|
||||
/// Whether this branch is redundant in the `when` it appears in
|
||||
pub redundant: RedundantMark,
|
||||
}
|
||||
|
||||
impl WhenBranch {
|
||||
|
@ -711,6 +749,7 @@ pub fn canonicalize_expr<'a>(
|
|||
loc_cond: Box::new(can_cond),
|
||||
branches: can_branches,
|
||||
branches_cond_var: var_store.fresh(),
|
||||
exhaustive: ExhaustiveMark::new(var_store),
|
||||
};
|
||||
|
||||
(expr, output)
|
||||
|
@ -992,11 +1031,15 @@ fn canonicalize_closure_body<'a>(
|
|||
loc_pattern.region,
|
||||
);
|
||||
|
||||
can_args.push((var_store.fresh(), can_argument_pattern));
|
||||
can_args.push((
|
||||
var_store.fresh(),
|
||||
AnnotatedMark::new(var_store),
|
||||
can_argument_pattern,
|
||||
));
|
||||
}
|
||||
|
||||
let bound_by_argument_patterns: Vec<_> =
|
||||
BindingsFromPattern::new_many(can_args.iter().map(|x| &x.1)).collect();
|
||||
BindingsFromPattern::new_many(can_args.iter().map(|x| &x.2)).collect();
|
||||
|
||||
let (loc_body_expr, new_output) = canonicalize_expr(
|
||||
env,
|
||||
|
@ -1130,6 +1173,7 @@ fn canonicalize_when_branch<'a>(
|
|||
patterns,
|
||||
value,
|
||||
guard,
|
||||
redundant: RedundantMark::new(var_store),
|
||||
},
|
||||
references,
|
||||
)
|
||||
|
@ -1342,6 +1386,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
|||
loc_cond,
|
||||
branches,
|
||||
branches_cond_var,
|
||||
exhaustive,
|
||||
} => {
|
||||
let loc_cond = Box::new(Loc {
|
||||
region: loc_cond.region,
|
||||
|
@ -1366,6 +1411,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
|||
patterns: branch.patterns,
|
||||
value,
|
||||
guard,
|
||||
redundant: RedundantMark::new(var_store),
|
||||
};
|
||||
|
||||
new_branches.push(new_branch);
|
||||
|
@ -1378,6 +1424,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
|||
loc_cond,
|
||||
branches: new_branches,
|
||||
branches_cond_var,
|
||||
exhaustive,
|
||||
}
|
||||
}
|
||||
If {
|
||||
|
@ -1607,7 +1654,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
|||
// Wrap the body in one LetNonRec for each argument,
|
||||
// such that at the end we have all the arguments in
|
||||
// scope with the values the caller provided.
|
||||
for ((_param_var, loc_pattern), (expr_var, loc_expr)) in
|
||||
for ((_param_var, _exhaustive_mark, loc_pattern), (expr_var, loc_expr)) in
|
||||
params.iter().cloned().zip(args.into_iter()).rev()
|
||||
{
|
||||
// TODO get the correct vars into here.
|
||||
|
|
|
@ -663,7 +663,7 @@ fn fix_values_captured_in_closure_expr(
|
|||
}
|
||||
|
||||
// patterns can contain default expressions, so much go over them too!
|
||||
for (_, loc_pat) in arguments.iter_mut() {
|
||||
for (_, _, loc_pat) in arguments.iter_mut() {
|
||||
fix_values_captured_in_closure_pattern(&mut loc_pat.value, no_capture_symbols);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ use roc_parse::pattern::PatternType;
|
|||
use roc_problem::can::{MalformedPatternProblem, Problem, RuntimeError, ShadowKind};
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::{VarStore, Variable};
|
||||
use roc_types::types::{LambdaSet, Type};
|
||||
use roc_types::types::{LambdaSet, PatternCategory, Type};
|
||||
|
||||
/// A pattern, including possible problems (e.g. shadowing) so that
|
||||
/// codegen can generate a runtime error if this pattern is reached.
|
||||
|
@ -105,6 +105,59 @@ impl Pattern {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Is this pattern sure to cover all instances of a type T, assuming it typechecks against T?
|
||||
pub fn surely_exhaustive(&self) -> bool {
|
||||
use Pattern::*;
|
||||
match self {
|
||||
Identifier(..)
|
||||
| Underscore
|
||||
| Shadowed(..)
|
||||
| OpaqueNotInScope(..)
|
||||
| UnsupportedPattern(..)
|
||||
| MalformedPattern(..)
|
||||
| AbilityMemberSpecialization { .. } => true,
|
||||
RecordDestructure { destructs, .. } => destructs.is_empty(),
|
||||
AppliedTag { .. }
|
||||
| NumLiteral(..)
|
||||
| IntLiteral(..)
|
||||
| FloatLiteral(..)
|
||||
| StrLiteral(..)
|
||||
| SingleQuote(..) => false,
|
||||
UnwrappedOpaque { argument, .. } => {
|
||||
// Opaques can only match against one constructor (the opaque symbol), so this is
|
||||
// surely exhaustive against T if the inner pattern is surely exhaustive against
|
||||
// its type U.
|
||||
argument.1.value.surely_exhaustive()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn category(&self) -> PatternCategory {
|
||||
use Pattern::*;
|
||||
use PatternCategory as C;
|
||||
|
||||
match self {
|
||||
Identifier(_) => C::PatternDefault,
|
||||
|
||||
AppliedTag { tag_name, .. } => C::Ctor(tag_name.clone()),
|
||||
UnwrappedOpaque { opaque, .. } => C::Opaque(*opaque),
|
||||
RecordDestructure { destructs, .. } if destructs.is_empty() => C::EmptyRecord,
|
||||
RecordDestructure { .. } => C::Record,
|
||||
NumLiteral(..) => C::Num,
|
||||
IntLiteral(..) => C::Int,
|
||||
FloatLiteral(..) => C::Float,
|
||||
StrLiteral(_) => C::Str,
|
||||
SingleQuote(_) => C::Character,
|
||||
Underscore => C::PatternDefault,
|
||||
|
||||
AbilityMemberSpecialization { .. } => C::PatternDefault,
|
||||
|
||||
Shadowed(..) | OpaqueNotInScope(..) | UnsupportedPattern(..) | MalformedPattern(..) => {
|
||||
C::PatternDefault
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
|
@ -66,6 +66,7 @@ fn walk_expr<V: Visitor>(visitor: &mut V, expr: &Expr) {
|
|||
branches,
|
||||
region: _,
|
||||
branches_cond_var: _,
|
||||
exhaustive: _,
|
||||
} => {
|
||||
walk_when(visitor, *cond_var, *expr_var, loc_cond, branches);
|
||||
}
|
||||
|
@ -81,9 +82,9 @@ fn walk_closure<V: Visitor>(visitor: &mut V, clos: &ClosureData) {
|
|||
..
|
||||
} = clos;
|
||||
|
||||
arguments
|
||||
.iter()
|
||||
.for_each(|(var, arg)| visitor.visit_pattern(&arg.value, arg.region, Some(*var)));
|
||||
arguments.iter().for_each(|(var, _exhaustive_mark, arg)| {
|
||||
visitor.visit_pattern(&arg.value, arg.region, Some(*var))
|
||||
});
|
||||
|
||||
visitor.visit_expr(&loc_body.value, loc_body.region, *return_type);
|
||||
}
|
||||
|
@ -107,6 +108,7 @@ fn walk_when_branch<V: Visitor>(visitor: &mut V, branch: &WhenBranch, expr_var:
|
|||
patterns,
|
||||
value,
|
||||
guard,
|
||||
redundant: _,
|
||||
} = branch;
|
||||
|
||||
patterns
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue