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
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -3937,7 +3937,6 @@ dependencies = [
|
||||||
"roc_exhaustive",
|
"roc_exhaustive",
|
||||||
"roc_load",
|
"roc_load",
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_mono",
|
|
||||||
"roc_parse",
|
"roc_parse",
|
||||||
"roc_problem",
|
"roc_problem",
|
||||||
"roc_region",
|
"roc_region",
|
||||||
|
|
|
@ -24,10 +24,6 @@ pub struct CodeGenTiming {
|
||||||
#[cfg(feature = "llvm")]
|
#[cfg(feature = "llvm")]
|
||||||
const LLVM_VERSION: &str = "12";
|
const LLVM_VERSION: &str = "12";
|
||||||
|
|
||||||
// TODO instead of finding exhaustiveness problems in monomorphization, find
|
|
||||||
// them after type checking (like Elm does) so we can complete the entire
|
|
||||||
// `roc check` process without needing to monomorphize.
|
|
||||||
/// Returns the number of problems reported.
|
|
||||||
pub fn report_problems_monomorphized(loaded: &mut MonomorphizedModule) -> Problems {
|
pub fn report_problems_monomorphized(loaded: &mut MonomorphizedModule) -> Problems {
|
||||||
report_problems_help(
|
report_problems_help(
|
||||||
loaded.total_problems(),
|
loaded.total_problems(),
|
||||||
|
@ -35,7 +31,6 @@ pub fn report_problems_monomorphized(loaded: &mut MonomorphizedModule) -> Proble
|
||||||
&loaded.interns,
|
&loaded.interns,
|
||||||
&mut loaded.can_problems,
|
&mut loaded.can_problems,
|
||||||
&mut loaded.type_problems,
|
&mut loaded.type_problems,
|
||||||
&mut loaded.mono_problems,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +41,6 @@ pub fn report_problems_typechecked(loaded: &mut LoadedModule) -> Problems {
|
||||||
&loaded.interns,
|
&loaded.interns,
|
||||||
&mut loaded.can_problems,
|
&mut loaded.can_problems,
|
||||||
&mut loaded.type_problems,
|
&mut loaded.type_problems,
|
||||||
&mut Default::default(),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,11 +67,9 @@ fn report_problems_help(
|
||||||
interns: &Interns,
|
interns: &Interns,
|
||||||
can_problems: &mut MutMap<ModuleId, Vec<roc_problem::can::Problem>>,
|
can_problems: &mut MutMap<ModuleId, Vec<roc_problem::can::Problem>>,
|
||||||
type_problems: &mut MutMap<ModuleId, Vec<roc_solve::solve::TypeError>>,
|
type_problems: &mut MutMap<ModuleId, Vec<roc_solve::solve::TypeError>>,
|
||||||
mono_problems: &mut MutMap<ModuleId, Vec<roc_mono::ir::MonoProblem>>,
|
|
||||||
) -> Problems {
|
) -> Problems {
|
||||||
use roc_reporting::report::{
|
use roc_reporting::report::{
|
||||||
can_problem, mono_problem, type_problem, Report, RocDocAllocator, Severity::*,
|
can_problem, type_problem, Report, RocDocAllocator, Severity::*, DEFAULT_PALETTE,
|
||||||
DEFAULT_PALETTE,
|
|
||||||
};
|
};
|
||||||
let palette = DEFAULT_PALETTE;
|
let palette = DEFAULT_PALETTE;
|
||||||
|
|
||||||
|
@ -134,25 +126,6 @@ fn report_problems_help(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let problems = mono_problems.remove(home).unwrap_or_default();
|
|
||||||
|
|
||||||
for problem in problems {
|
|
||||||
let report = mono_problem(&alloc, &lines, module_path.clone(), problem);
|
|
||||||
let severity = report.severity;
|
|
||||||
let mut buf = String::new();
|
|
||||||
|
|
||||||
report.render_color_terminal(&mut buf, &alloc, &palette);
|
|
||||||
|
|
||||||
match severity {
|
|
||||||
Warning => {
|
|
||||||
warnings.push(buf);
|
|
||||||
}
|
|
||||||
RuntimeError => {
|
|
||||||
errors.push(buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let problems_reported;
|
let problems_reported;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::def::Def;
|
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::expr::{Expr, Field, Recursive};
|
||||||
use crate::num::{FloatBound, IntBound, IntWidth, NumericBound};
|
use crate::num::{FloatBound, IntBound, IntWidth, NumericBound};
|
||||||
use crate::pattern::Pattern;
|
use crate::pattern::Pattern;
|
||||||
|
@ -9,7 +9,7 @@ use roc_module::ident::{Lowercase, TagName};
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_types::subs::{VarStore, Variable};
|
use roc_types::subs::{ExhaustiveMark, RedundantMark, VarStore, Variable};
|
||||||
|
|
||||||
macro_rules! macro_magic {
|
macro_rules! macro_magic {
|
||||||
(@single $($x:tt)*) => (());
|
(@single $($x:tt)*) => (());
|
||||||
|
@ -2622,8 +2622,16 @@ fn list_intersperse(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
recursive: Recursive::NotRecursive,
|
recursive: Recursive::NotRecursive,
|
||||||
captured_symbols: vec![(sep_sym, sep_var)],
|
captured_symbols: vec![(sep_sym, sep_var)],
|
||||||
arguments: vec![
|
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: {
|
loc_body: {
|
||||||
let append_sep = RunLowLevel {
|
let append_sep = RunLowLevel {
|
||||||
|
@ -2708,9 +2716,14 @@ fn list_split(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
arguments: vec![
|
arguments: vec![
|
||||||
(
|
(
|
||||||
clos_start_var,
|
clos_start_var,
|
||||||
|
AnnotatedMark::new(var_store),
|
||||||
no_region(Pattern::Identifier(clos_start_sym)),
|
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: {
|
loc_body: {
|
||||||
Box::new(no_region(RunLowLevel {
|
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,
|
name: Symbol::LIST_DROP_IF_PREDICATE,
|
||||||
recursive: Recursive::NotRecursive,
|
recursive: Recursive::NotRecursive,
|
||||||
captured_symbols: vec![(sym_predicate, t_predicate)],
|
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: {
|
loc_body: {
|
||||||
let should_drop = Call(
|
let should_drop = Call(
|
||||||
Box::new((
|
Box::new((
|
||||||
|
@ -3078,8 +3095,16 @@ fn list_join_map(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
recursive: Recursive::NotRecursive,
|
recursive: Recursive::NotRecursive,
|
||||||
captured_symbols: vec![(Symbol::ARG_2, before2list_after)],
|
captured_symbols: vec![(Symbol::ARG_2, before2list_after)],
|
||||||
arguments: vec![
|
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: {
|
loc_body: {
|
||||||
let mapper = Box::new((
|
let mapper = Box::new((
|
||||||
|
@ -3609,8 +3634,16 @@ fn list_sort_desc(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
recursive: Recursive::NotRecursive,
|
recursive: Recursive::NotRecursive,
|
||||||
captured_symbols: vec![],
|
captured_symbols: vec![],
|
||||||
arguments: 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: {
|
loc_body: {
|
||||||
Box::new(no_region(RunLowLevel {
|
Box::new(no_region(RunLowLevel {
|
||||||
|
@ -4084,9 +4117,21 @@ fn set_walk(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
recursive: Recursive::NotRecursive,
|
recursive: Recursive::NotRecursive,
|
||||||
captured_symbols: vec![(Symbol::ARG_3, func_var)],
|
captured_symbols: vec![(Symbol::ARG_3, func_var)],
|
||||||
arguments: vec![
|
arguments: vec![
|
||||||
(accum_var, no_region(Pattern::Identifier(Symbol::ARG_5))),
|
(
|
||||||
(key_var, no_region(Pattern::Identifier(Symbol::ARG_6))),
|
accum_var,
|
||||||
(Variable::EMPTY_RECORD, no_region(Pattern::Underscore)),
|
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)),
|
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)],
|
patterns: vec![no_region(pattern)],
|
||||||
value: no_region(ok),
|
value: no_region(ok),
|
||||||
guard: None,
|
guard: None,
|
||||||
|
redundant: RedundantMark::new(var_store),
|
||||||
};
|
};
|
||||||
|
|
||||||
branches.push(branch);
|
branches.push(branch);
|
||||||
|
@ -4716,6 +4762,7 @@ fn result_map(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
patterns: vec![no_region(pattern)],
|
patterns: vec![no_region(pattern)],
|
||||||
value: no_region(err),
|
value: no_region(err),
|
||||||
guard: None,
|
guard: None,
|
||||||
|
redundant: RedundantMark::new(var_store),
|
||||||
};
|
};
|
||||||
|
|
||||||
branches.push(branch);
|
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))),
|
loc_cond: Box::new(no_region(Var(Symbol::ARG_1))),
|
||||||
branches,
|
branches,
|
||||||
branches_cond_var: var_store.fresh(),
|
branches_cond_var: var_store.fresh(),
|
||||||
|
exhaustive: ExhaustiveMark::new(var_store),
|
||||||
};
|
};
|
||||||
|
|
||||||
defn(
|
defn(
|
||||||
|
@ -4784,6 +4832,7 @@ fn result_map_err(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
patterns: vec![no_region(pattern)],
|
patterns: vec![no_region(pattern)],
|
||||||
value: no_region(ok),
|
value: no_region(ok),
|
||||||
guard: None,
|
guard: None,
|
||||||
|
redundant: RedundantMark::new(var_store),
|
||||||
};
|
};
|
||||||
|
|
||||||
branches.push(branch);
|
branches.push(branch);
|
||||||
|
@ -4814,6 +4863,7 @@ fn result_map_err(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
patterns: vec![no_region(pattern)],
|
patterns: vec![no_region(pattern)],
|
||||||
value: no_region(err),
|
value: no_region(err),
|
||||||
guard: None,
|
guard: None,
|
||||||
|
redundant: RedundantMark::new(var_store),
|
||||||
};
|
};
|
||||||
|
|
||||||
branches.push(branch);
|
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))),
|
loc_cond: Box::new(no_region(Var(Symbol::ARG_1))),
|
||||||
branches,
|
branches,
|
||||||
branches_cond_var: var_store.fresh(),
|
branches_cond_var: var_store.fresh(),
|
||||||
|
exhaustive: ExhaustiveMark::new(var_store),
|
||||||
};
|
};
|
||||||
|
|
||||||
defn(
|
defn(
|
||||||
|
@ -4858,6 +4909,7 @@ fn result_with_default(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
patterns: vec![no_region(pattern)],
|
patterns: vec![no_region(pattern)],
|
||||||
value: no_region(Var(Symbol::ARG_3)),
|
value: no_region(Var(Symbol::ARG_3)),
|
||||||
guard: None,
|
guard: None,
|
||||||
|
redundant: RedundantMark::new(var_store),
|
||||||
};
|
};
|
||||||
|
|
||||||
branches.push(branch);
|
branches.push(branch);
|
||||||
|
@ -4878,6 +4930,7 @@ fn result_with_default(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
patterns: vec![no_region(pattern)],
|
patterns: vec![no_region(pattern)],
|
||||||
value: no_region(Var(Symbol::ARG_2)),
|
value: no_region(Var(Symbol::ARG_2)),
|
||||||
guard: None,
|
guard: None,
|
||||||
|
redundant: RedundantMark::new(var_store),
|
||||||
};
|
};
|
||||||
|
|
||||||
branches.push(branch);
|
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))),
|
loc_cond: Box::new(no_region(Var(Symbol::ARG_1))),
|
||||||
branches,
|
branches,
|
||||||
branches_cond_var: var_store.fresh(),
|
branches_cond_var: var_store.fresh(),
|
||||||
|
exhaustive: ExhaustiveMark::new(var_store),
|
||||||
};
|
};
|
||||||
|
|
||||||
defn(
|
defn(
|
||||||
|
@ -4929,6 +4983,7 @@ fn result_is_err(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
patterns: vec![no_region(pattern)],
|
patterns: vec![no_region(pattern)],
|
||||||
value: no_region(false_expr),
|
value: no_region(false_expr),
|
||||||
guard: None,
|
guard: None,
|
||||||
|
redundant: RedundantMark::new(var_store),
|
||||||
};
|
};
|
||||||
|
|
||||||
branches.push(branch);
|
branches.push(branch);
|
||||||
|
@ -4956,6 +5011,7 @@ fn result_is_err(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
patterns: vec![no_region(pattern)],
|
patterns: vec![no_region(pattern)],
|
||||||
value: no_region(true_expr),
|
value: no_region(true_expr),
|
||||||
guard: None,
|
guard: None,
|
||||||
|
redundant: RedundantMark::new(var_store),
|
||||||
};
|
};
|
||||||
|
|
||||||
branches.push(branch);
|
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))),
|
loc_cond: Box::new(no_region(Var(Symbol::ARG_1))),
|
||||||
branches,
|
branches,
|
||||||
branches_cond_var: var_store.fresh(),
|
branches_cond_var: var_store.fresh(),
|
||||||
|
exhaustive: ExhaustiveMark::new(var_store),
|
||||||
};
|
};
|
||||||
|
|
||||||
defn(
|
defn(
|
||||||
|
@ -5007,6 +5064,7 @@ fn result_is_ok(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
patterns: vec![no_region(pattern)],
|
patterns: vec![no_region(pattern)],
|
||||||
value: no_region(true_expr),
|
value: no_region(true_expr),
|
||||||
guard: None,
|
guard: None,
|
||||||
|
redundant: RedundantMark::new(var_store),
|
||||||
};
|
};
|
||||||
|
|
||||||
branches.push(branch);
|
branches.push(branch);
|
||||||
|
@ -5034,6 +5092,7 @@ fn result_is_ok(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
patterns: vec![no_region(pattern)],
|
patterns: vec![no_region(pattern)],
|
||||||
value: no_region(false_expr),
|
value: no_region(false_expr),
|
||||||
guard: None,
|
guard: None,
|
||||||
|
redundant: RedundantMark::new(var_store),
|
||||||
};
|
};
|
||||||
|
|
||||||
branches.push(branch);
|
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))),
|
loc_cond: Box::new(no_region(Var(Symbol::ARG_1))),
|
||||||
branches,
|
branches,
|
||||||
branches_cond_var: var_store.fresh(),
|
branches_cond_var: var_store.fresh(),
|
||||||
|
exhaustive: ExhaustiveMark::new(var_store),
|
||||||
};
|
};
|
||||||
|
|
||||||
defn(
|
defn(
|
||||||
|
@ -5097,6 +5157,7 @@ fn result_after(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
patterns: vec![no_region(pattern)],
|
patterns: vec![no_region(pattern)],
|
||||||
value: no_region(ok),
|
value: no_region(ok),
|
||||||
guard: None,
|
guard: None,
|
||||||
|
redundant: RedundantMark::new(var_store),
|
||||||
};
|
};
|
||||||
|
|
||||||
branches.push(branch);
|
branches.push(branch);
|
||||||
|
@ -5127,6 +5188,7 @@ fn result_after(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
patterns: vec![no_region(pattern)],
|
patterns: vec![no_region(pattern)],
|
||||||
value: no_region(err),
|
value: no_region(err),
|
||||||
guard: None,
|
guard: None,
|
||||||
|
redundant: RedundantMark::new(var_store),
|
||||||
};
|
};
|
||||||
|
|
||||||
branches.push(branch);
|
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))),
|
loc_cond: Box::new(no_region(Var(Symbol::ARG_1))),
|
||||||
branches,
|
branches,
|
||||||
branches_cond_var: var_store.fresh(),
|
branches_cond_var: var_store.fresh(),
|
||||||
|
exhaustive: ExhaustiveMark::new(var_store),
|
||||||
};
|
};
|
||||||
|
|
||||||
defn(
|
defn(
|
||||||
|
@ -5324,7 +5387,13 @@ fn defn_help(
|
||||||
|
|
||||||
let closure_args = args
|
let closure_args = args
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(var, symbol)| (var, no_region(Identifier(symbol))))
|
.map(|(var, symbol)| {
|
||||||
|
(
|
||||||
|
var,
|
||||||
|
AnnotatedMark::new(var_store),
|
||||||
|
no_region(Identifier(symbol)),
|
||||||
|
)
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Closure(ClosureData {
|
Closure(ClosureData {
|
||||||
|
|
|
@ -4,7 +4,7 @@ use roc_collections::soa::{EitherIndex, Index, Slice};
|
||||||
use roc_module::ident::TagName;
|
use roc_module::ident::TagName;
|
||||||
use roc_module::symbol::{ModuleId, Symbol};
|
use roc_module::symbol::{ModuleId, Symbol};
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_types::subs::Variable;
|
use roc_types::subs::{ExhaustiveMark, Variable};
|
||||||
use roc_types::types::{Category, PatternCategory, Type};
|
use roc_types::types::{Category, PatternCategory, Type};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -22,6 +22,7 @@ pub struct Constraints {
|
||||||
pub strings: Vec<&'static str>,
|
pub strings: Vec<&'static str>,
|
||||||
pub sketched_rows: Vec<SketchedRows>,
|
pub sketched_rows: Vec<SketchedRows>,
|
||||||
pub eq: Vec<Eq>,
|
pub eq: Vec<Eq>,
|
||||||
|
pub pattern_eq: Vec<PatternEq>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Constraints {
|
impl Default for Constraints {
|
||||||
|
@ -45,6 +46,7 @@ impl Constraints {
|
||||||
let strings = Vec::new();
|
let strings = Vec::new();
|
||||||
let sketched_rows = Vec::new();
|
let sketched_rows = Vec::new();
|
||||||
let eq = Vec::new();
|
let eq = Vec::new();
|
||||||
|
let pattern_eq = Vec::new();
|
||||||
|
|
||||||
types.extend([
|
types.extend([
|
||||||
Type::EmptyRec,
|
Type::EmptyRec,
|
||||||
|
@ -97,6 +99,7 @@ impl Constraints {
|
||||||
strings,
|
strings,
|
||||||
sketched_rows,
|
sketched_rows,
|
||||||
eq,
|
eq,
|
||||||
|
pattern_eq,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -610,19 +613,35 @@ impl Constraints {
|
||||||
&mut self,
|
&mut self,
|
||||||
real_var: Variable,
|
real_var: Variable,
|
||||||
real_region: Region,
|
real_region: Region,
|
||||||
real_category: Category,
|
category_and_expectation: Result<
|
||||||
expected_branches: Expected<Type>,
|
(Category, Expected<Type>),
|
||||||
|
(PatternCategory, PExpected<Type>),
|
||||||
|
>,
|
||||||
sketched_rows: SketchedRows,
|
sketched_rows: SketchedRows,
|
||||||
context: ExhaustiveContext,
|
context: ExhaustiveContext,
|
||||||
|
exhaustive: ExhaustiveMark,
|
||||||
) -> Constraint {
|
) -> Constraint {
|
||||||
let real_var = Self::push_type_variable(real_var);
|
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);
|
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,
|
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)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum Constraint {
|
pub enum Constraint {
|
||||||
Eq(Eq),
|
Eq(Eq),
|
||||||
|
@ -672,7 +699,12 @@ pub enum Constraint {
|
||||||
Index<PatternCategory>,
|
Index<PatternCategory>,
|
||||||
Region,
|
Region,
|
||||||
),
|
),
|
||||||
Exhaustive(Index<Eq>, Index<SketchedRows>, ExhaustiveContext),
|
Exhaustive(
|
||||||
|
Result<Index<Eq>, Index<PatternEq>>,
|
||||||
|
Index<SketchedRows>,
|
||||||
|
ExhaustiveContext,
|
||||||
|
ExhaustiveMark,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
@ -727,8 +759,12 @@ impl std::fmt::Debug for Constraint {
|
||||||
arg0, arg1, arg2, arg3
|
arg0, arg1, arg2, arg3
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Self::Exhaustive(arg0, arg1, arg2) => {
|
Self::Exhaustive(arg0, arg1, arg2, arg3) => {
|
||||||
write!(f, "Exhaustive({:?}, {:?}, {:?})", arg0, arg1, arg2)
|
write!(
|
||||||
|
f,
|
||||||
|
"Exhaustive({:?}, {:?}, {:?}, {:?})",
|
||||||
|
arg0, arg1, arg2, arg3
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ use crate::abilities::MemberVariables;
|
||||||
use crate::annotation::canonicalize_annotation;
|
use crate::annotation::canonicalize_annotation;
|
||||||
use crate::annotation::IntroducedVariables;
|
use crate::annotation::IntroducedVariables;
|
||||||
use crate::env::Env;
|
use crate::env::Env;
|
||||||
|
use crate::expr::AnnotatedMark;
|
||||||
use crate::expr::ClosureData;
|
use crate::expr::ClosureData;
|
||||||
use crate::expr::Expr::{self, *};
|
use crate::expr::Expr::{self, *};
|
||||||
use crate::expr::{canonicalize_expr, Output, Recursive};
|
use crate::expr::{canonicalize_expr, Output, Recursive};
|
||||||
|
@ -1085,7 +1086,11 @@ fn canonicalize_pending_value_def<'a>(
|
||||||
region: Region::zero(),
|
region: Region::zero(),
|
||||||
};
|
};
|
||||||
|
|
||||||
underscores.push((var_store.fresh(), underscore));
|
underscores.push((
|
||||||
|
var_store.fresh(),
|
||||||
|
AnnotatedMark::known_exhaustive(),
|
||||||
|
underscore,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let body_expr = Loc {
|
let body_expr = Loc {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::annotation::IntroducedVariables;
|
use crate::annotation::IntroducedVariables;
|
||||||
use crate::def::{Declaration, Def};
|
use crate::def::{Declaration, Def};
|
||||||
use crate::expr::{ClosureData, Expr, Recursive};
|
use crate::expr::{AnnotatedMark, ClosureData, Expr, Recursive};
|
||||||
use crate::pattern::Pattern;
|
use crate::pattern::Pattern;
|
||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
use roc_collections::{SendMap, VecSet};
|
use roc_collections::{SendMap, VecSet};
|
||||||
|
@ -8,7 +8,7 @@ use roc_module::called_via::CalledVia;
|
||||||
use roc_module::ident::{Lowercase, TagName};
|
use roc_module::ident::{Lowercase, TagName};
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_region::all::{Loc, Region};
|
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};
|
use roc_types::types::{AliasKind, LambdaSet, Type, TypeExtension};
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy)]
|
#[derive(Debug, Default, Clone, Copy)]
|
||||||
|
@ -120,6 +120,7 @@ fn build_effect_always(
|
||||||
let const_closure = {
|
let const_closure = {
|
||||||
let arguments = vec![(
|
let arguments = vec![(
|
||||||
var_store.fresh(),
|
var_store.fresh(),
|
||||||
|
AnnotatedMark::new(var_store),
|
||||||
Loc::at_zero(empty_record_pattern(var_store)),
|
Loc::at_zero(empty_record_pattern(var_store)),
|
||||||
)];
|
)];
|
||||||
|
|
||||||
|
@ -154,6 +155,7 @@ fn build_effect_always(
|
||||||
|
|
||||||
let arguments = vec![(
|
let arguments = vec![(
|
||||||
var_store.fresh(),
|
var_store.fresh(),
|
||||||
|
AnnotatedMark::new(var_store),
|
||||||
Loc::at_zero(Pattern::Identifier(value_symbol)),
|
Loc::at_zero(Pattern::Identifier(value_symbol)),
|
||||||
)];
|
)];
|
||||||
|
|
||||||
|
@ -277,6 +279,7 @@ fn build_effect_map(
|
||||||
let inner_closure = {
|
let inner_closure = {
|
||||||
let arguments = vec![(
|
let arguments = vec![(
|
||||||
var_store.fresh(),
|
var_store.fresh(),
|
||||||
|
AnnotatedMark::new(var_store),
|
||||||
Loc::at_zero(empty_record_pattern(var_store)),
|
Loc::at_zero(empty_record_pattern(var_store)),
|
||||||
)];
|
)];
|
||||||
|
|
||||||
|
@ -302,6 +305,7 @@ fn build_effect_map(
|
||||||
let arguments = vec![
|
let arguments = vec![
|
||||||
(
|
(
|
||||||
var_store.fresh(),
|
var_store.fresh(),
|
||||||
|
AnnotatedMark::new(var_store),
|
||||||
Loc::at_zero(Pattern::UnwrappedOpaque {
|
Loc::at_zero(Pattern::UnwrappedOpaque {
|
||||||
opaque: effect_symbol,
|
opaque: effect_symbol,
|
||||||
whole_var: var_store.fresh(),
|
whole_var: var_store.fresh(),
|
||||||
|
@ -316,6 +320,7 @@ fn build_effect_map(
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
var_store.fresh(),
|
var_store.fresh(),
|
||||||
|
AnnotatedMark::new(var_store),
|
||||||
Loc::at_zero(Pattern::Identifier(mapper_symbol)),
|
Loc::at_zero(Pattern::Identifier(mapper_symbol)),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
@ -466,6 +471,7 @@ fn build_effect_after(
|
||||||
let arguments = vec![
|
let arguments = vec![
|
||||||
(
|
(
|
||||||
var_store.fresh(),
|
var_store.fresh(),
|
||||||
|
AnnotatedMark::new(var_store),
|
||||||
Loc::at_zero(Pattern::UnwrappedOpaque {
|
Loc::at_zero(Pattern::UnwrappedOpaque {
|
||||||
opaque: effect_symbol,
|
opaque: effect_symbol,
|
||||||
whole_var: var_store.fresh(),
|
whole_var: var_store.fresh(),
|
||||||
|
@ -480,6 +486,7 @@ fn build_effect_after(
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
var_store.fresh(),
|
var_store.fresh(),
|
||||||
|
AnnotatedMark::new(var_store),
|
||||||
Loc::at_zero(Pattern::Identifier(to_effect_symbol)),
|
Loc::at_zero(Pattern::Identifier(to_effect_symbol)),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
@ -579,6 +586,7 @@ fn wrap_in_effect_thunk(
|
||||||
let const_closure = {
|
let const_closure = {
|
||||||
let arguments = vec![(
|
let arguments = vec![(
|
||||||
var_store.fresh(),
|
var_store.fresh(),
|
||||||
|
AnnotatedMark::new(var_store),
|
||||||
Loc::at_zero(empty_record_pattern(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 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 function_var = var_store.fresh();
|
||||||
let after_closure = Expr::Closure(ClosureData {
|
let after_closure = Expr::Closure(ClosureData {
|
||||||
|
@ -939,10 +951,12 @@ fn build_effect_loop(
|
||||||
let arguments = vec![
|
let arguments = vec![
|
||||||
(
|
(
|
||||||
var_store.fresh(),
|
var_store.fresh(),
|
||||||
|
AnnotatedMark::new(var_store),
|
||||||
Loc::at_zero(Pattern::Identifier(state_symbol)),
|
Loc::at_zero(Pattern::Identifier(state_symbol)),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
var_store.fresh(),
|
var_store.fresh(),
|
||||||
|
AnnotatedMark::new(var_store),
|
||||||
Loc::at_zero(Pattern::Identifier(step_symbol)),
|
Loc::at_zero(Pattern::Identifier(step_symbol)),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
@ -1209,6 +1223,7 @@ fn build_effect_loop_inner_body(
|
||||||
patterns: vec![Loc::at_zero(step_pattern)],
|
patterns: vec![Loc::at_zero(step_pattern)],
|
||||||
value: Loc::at_zero(force_thunk2),
|
value: Loc::at_zero(force_thunk2),
|
||||||
guard: None,
|
guard: None,
|
||||||
|
redundant: RedundantMark::new(var_store),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1220,6 +1235,7 @@ fn build_effect_loop_inner_body(
|
||||||
patterns: vec![Loc::at_zero(done_pattern)],
|
patterns: vec![Loc::at_zero(done_pattern)],
|
||||||
value: Loc::at_zero(Expr::Var(done_symbol)),
|
value: Loc::at_zero(Expr::Var(done_symbol)),
|
||||||
guard: None,
|
guard: None,
|
||||||
|
redundant: RedundantMark::new(var_store),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1232,6 +1248,7 @@ fn build_effect_loop_inner_body(
|
||||||
loc_cond: Box::new(force_thunk_call),
|
loc_cond: Box::new(force_thunk_call),
|
||||||
branches,
|
branches,
|
||||||
branches_cond_var: var_store.fresh(),
|
branches_cond_var: var_store.fresh(),
|
||||||
|
exhaustive: ExhaustiveMark::new(var_store),
|
||||||
};
|
};
|
||||||
|
|
||||||
Expr::LetNonRec(
|
Expr::LetNonRec(
|
||||||
|
@ -1254,7 +1271,7 @@ pub fn build_host_exposed_def(
|
||||||
let mut pattern_vars = SendMap::default();
|
let mut pattern_vars = SendMap::default();
|
||||||
pattern_vars.insert(symbol, expr_var);
|
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 linked_symbol_arguments: Vec<(Variable, Expr)> = Vec::new();
|
||||||
let mut captured_symbols: Vec<(Symbol, Variable)> = 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();
|
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));
|
captured_symbols.push((arg_symbol, arg_var));
|
||||||
linked_symbol_arguments.push((arg_var, Expr::Var(arg_symbol)));
|
linked_symbol_arguments.push((arg_var, Expr::Var(arg_symbol)));
|
||||||
|
@ -1308,6 +1329,7 @@ pub fn build_host_exposed_def(
|
||||||
recursive: Recursive::NotRecursive,
|
recursive: Recursive::NotRecursive,
|
||||||
arguments: vec![(
|
arguments: vec![(
|
||||||
var_store.fresh(),
|
var_store.fresh(),
|
||||||
|
AnnotatedMark::new(var_store),
|
||||||
Loc::at_zero(empty_record_pattern(var_store)),
|
Loc::at_zero(empty_record_pattern(var_store)),
|
||||||
)],
|
)],
|
||||||
loc_body: Box::new(Loc::at_zero(low_level_call)),
|
loc_body: Box::new(Loc::at_zero(low_level_call)),
|
||||||
|
@ -1367,7 +1389,11 @@ pub fn build_host_exposed_def(
|
||||||
name: effect_closure_symbol,
|
name: effect_closure_symbol,
|
||||||
captured_symbols,
|
captured_symbols,
|
||||||
recursive: Recursive::NotRecursive,
|
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)),
|
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_module::ident::{TagIdIntType, TagName};
|
||||||
use roc_region::all::{Loc, Region};
|
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;
|
use roc_types::types::AliasKind;
|
||||||
|
|
||||||
pub use roc_exhaustive::Context as ExhaustiveContext;
|
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 GUARD_CTOR: &str = "#Guard";
|
||||||
pub const NONEXHAUSIVE_CTOR: &str = "#Open";
|
pub const NONEXHAUSIVE_CTOR: &str = "#Open";
|
||||||
|
|
||||||
|
pub struct ExhaustiveSummary {
|
||||||
|
pub errors: Vec<Error>,
|
||||||
|
pub exhaustive: bool,
|
||||||
|
pub redundancies: Vec<RedundantMark>,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn check(
|
pub fn check(
|
||||||
subs: &Subs,
|
subs: &Subs,
|
||||||
sketched_rows: SketchedRows,
|
sketched_rows: SketchedRows,
|
||||||
context: ExhaustiveContext,
|
context: ExhaustiveContext,
|
||||||
) -> Result<(), Vec<Error>> {
|
) -> ExhaustiveSummary {
|
||||||
let overall_region = sketched_rows.overall_region;
|
let overall_region = sketched_rows.overall_region;
|
||||||
// TODO: can we keep going even if we had redundant rows?
|
let mut all_errors = Vec::with_capacity(1);
|
||||||
let matrix = sketched_rows
|
|
||||||
.reify_to_non_redundant(subs)
|
let NonRedundantSummary {
|
||||||
.map_err(|e| vec![e])?;
|
non_redundant_rows,
|
||||||
roc_exhaustive::check(overall_region, context, matrix)
|
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)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
@ -63,6 +86,7 @@ struct SketchedRow {
|
||||||
patterns: Vec<SketchedPattern>,
|
patterns: Vec<SketchedPattern>,
|
||||||
region: Region,
|
region: Region,
|
||||||
guard: Guard,
|
guard: Guard,
|
||||||
|
redundant_mark: RedundantMark,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
@ -72,7 +96,7 @@ pub struct SketchedRows {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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)
|
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());
|
let mut rows: Vec<SketchedRow> = Vec::with_capacity(patterns.len());
|
||||||
|
|
||||||
// If any of the branches has a guard, e.g.
|
// 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,
|
patterns,
|
||||||
guard,
|
guard,
|
||||||
value: _,
|
value: _,
|
||||||
|
redundant,
|
||||||
} in patterns
|
} in patterns
|
||||||
{
|
{
|
||||||
let guard = if guard.is_some() {
|
let guard = if guard.is_some() {
|
||||||
|
@ -242,6 +271,7 @@ pub fn sketch_rows(target_var: Variable, region: Region, patterns: &[WhenBranch]
|
||||||
patterns,
|
patterns,
|
||||||
region: loc_pat.region,
|
region: loc_pat.region,
|
||||||
guard,
|
guard,
|
||||||
|
redundant_mark: *redundant,
|
||||||
};
|
};
|
||||||
rows.push(row);
|
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
|
/// 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)
|
/// 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 {
|
let SketchedRows {
|
||||||
rows,
|
rows,
|
||||||
overall_region,
|
overall_region,
|
||||||
} = rows;
|
} = rows;
|
||||||
let mut checked_rows = Vec::with_capacity(rows.len());
|
let mut checked_rows = Vec::with_capacity(rows.len());
|
||||||
|
|
||||||
|
let mut redundancies = vec![];
|
||||||
|
let mut errors = vec![];
|
||||||
|
|
||||||
for SketchedRow {
|
for SketchedRow {
|
||||||
patterns,
|
patterns,
|
||||||
guard,
|
guard,
|
||||||
region,
|
region,
|
||||||
|
redundant_mark,
|
||||||
} in rows.into_iter()
|
} in rows.into_iter()
|
||||||
{
|
{
|
||||||
let next_row: Vec<Pattern> = patterns
|
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()) {
|
if matches!(guard, Guard::HasGuard) || is_useful(checked_rows.clone(), next_row.clone()) {
|
||||||
checked_rows.push(next_row);
|
checked_rows.push(next_row);
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::Redundant {
|
redundancies.push(redundant_mark);
|
||||||
|
errors.push(Error::Redundant {
|
||||||
overall_region,
|
overall_region,
|
||||||
branch_region: region,
|
branch_region: region,
|
||||||
index: HumanIndex::zero_based(checked_rows.len()),
|
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) {
|
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_parse::pattern::PatternType::*;
|
||||||
use roc_problem::can::{PrecedenceProblem, Problem, RuntimeError};
|
use roc_problem::can::{PrecedenceProblem, Problem, RuntimeError};
|
||||||
use roc_region::all::{Loc, Region};
|
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 roc_types::types::{Alias, Category, LambdaSet, Type};
|
||||||
use std::fmt::{Debug, Display};
|
use std::fmt::{Debug, Display};
|
||||||
use std::{char, u32};
|
use std::{char, u32};
|
||||||
|
@ -84,12 +84,18 @@ pub enum Expr {
|
||||||
Var(Symbol),
|
Var(Symbol),
|
||||||
// Branching
|
// Branching
|
||||||
When {
|
When {
|
||||||
|
/// The actual condition of the when expression.
|
||||||
|
loc_cond: Box<Loc<Expr>>,
|
||||||
cond_var: Variable,
|
cond_var: Variable,
|
||||||
|
/// Result type produced by the branches.
|
||||||
expr_var: Variable,
|
expr_var: Variable,
|
||||||
region: Region,
|
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: Vec<WhenBranch>,
|
||||||
branches_cond_var: Variable,
|
branches_cond_var: Variable,
|
||||||
|
/// Whether the branches are exhaustive.
|
||||||
|
exhaustive: ExhaustiveMark,
|
||||||
},
|
},
|
||||||
If {
|
If {
|
||||||
cond_var: Variable,
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ClosureData {
|
pub struct ClosureData {
|
||||||
pub function_type: Variable,
|
pub function_type: Variable,
|
||||||
|
@ -244,7 +276,7 @@ pub struct ClosureData {
|
||||||
pub name: Symbol,
|
pub name: Symbol,
|
||||||
pub captured_symbols: Vec<(Symbol, Variable)>,
|
pub captured_symbols: Vec<(Symbol, Variable)>,
|
||||||
pub recursive: Recursive,
|
pub recursive: Recursive,
|
||||||
pub arguments: Vec<(Variable, Loc<Pattern>)>,
|
pub arguments: Vec<(Variable, AnnotatedMark, Loc<Pattern>)>,
|
||||||
pub loc_body: Box<Loc<Expr>>,
|
pub loc_body: Box<Loc<Expr>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,7 +328,11 @@ impl AccessorData {
|
||||||
|
|
||||||
let loc_body = Loc::at_zero(body);
|
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 {
|
ClosureData {
|
||||||
function_type: function_var,
|
function_type: function_var,
|
||||||
|
@ -332,6 +368,8 @@ pub struct WhenBranch {
|
||||||
pub patterns: Vec<Loc<Pattern>>,
|
pub patterns: Vec<Loc<Pattern>>,
|
||||||
pub value: Loc<Expr>,
|
pub value: Loc<Expr>,
|
||||||
pub guard: Option<Loc<Expr>>,
|
pub guard: Option<Loc<Expr>>,
|
||||||
|
/// Whether this branch is redundant in the `when` it appears in
|
||||||
|
pub redundant: RedundantMark,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WhenBranch {
|
impl WhenBranch {
|
||||||
|
@ -711,6 +749,7 @@ pub fn canonicalize_expr<'a>(
|
||||||
loc_cond: Box::new(can_cond),
|
loc_cond: Box::new(can_cond),
|
||||||
branches: can_branches,
|
branches: can_branches,
|
||||||
branches_cond_var: var_store.fresh(),
|
branches_cond_var: var_store.fresh(),
|
||||||
|
exhaustive: ExhaustiveMark::new(var_store),
|
||||||
};
|
};
|
||||||
|
|
||||||
(expr, output)
|
(expr, output)
|
||||||
|
@ -992,11 +1031,15 @@ fn canonicalize_closure_body<'a>(
|
||||||
loc_pattern.region,
|
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<_> =
|
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(
|
let (loc_body_expr, new_output) = canonicalize_expr(
|
||||||
env,
|
env,
|
||||||
|
@ -1130,6 +1173,7 @@ fn canonicalize_when_branch<'a>(
|
||||||
patterns,
|
patterns,
|
||||||
value,
|
value,
|
||||||
guard,
|
guard,
|
||||||
|
redundant: RedundantMark::new(var_store),
|
||||||
},
|
},
|
||||||
references,
|
references,
|
||||||
)
|
)
|
||||||
|
@ -1342,6 +1386,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
||||||
loc_cond,
|
loc_cond,
|
||||||
branches,
|
branches,
|
||||||
branches_cond_var,
|
branches_cond_var,
|
||||||
|
exhaustive,
|
||||||
} => {
|
} => {
|
||||||
let loc_cond = Box::new(Loc {
|
let loc_cond = Box::new(Loc {
|
||||||
region: loc_cond.region,
|
region: loc_cond.region,
|
||||||
|
@ -1366,6 +1411,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
||||||
patterns: branch.patterns,
|
patterns: branch.patterns,
|
||||||
value,
|
value,
|
||||||
guard,
|
guard,
|
||||||
|
redundant: RedundantMark::new(var_store),
|
||||||
};
|
};
|
||||||
|
|
||||||
new_branches.push(new_branch);
|
new_branches.push(new_branch);
|
||||||
|
@ -1378,6 +1424,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
||||||
loc_cond,
|
loc_cond,
|
||||||
branches: new_branches,
|
branches: new_branches,
|
||||||
branches_cond_var,
|
branches_cond_var,
|
||||||
|
exhaustive,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
If {
|
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,
|
// Wrap the body in one LetNonRec for each argument,
|
||||||
// such that at the end we have all the arguments in
|
// such that at the end we have all the arguments in
|
||||||
// scope with the values the caller provided.
|
// 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()
|
params.iter().cloned().zip(args.into_iter()).rev()
|
||||||
{
|
{
|
||||||
// TODO get the correct vars into here.
|
// 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!
|
// 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);
|
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_problem::can::{MalformedPatternProblem, Problem, RuntimeError, ShadowKind};
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_types::subs::{VarStore, Variable};
|
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
|
/// A pattern, including possible problems (e.g. shadowing) so that
|
||||||
/// codegen can generate a runtime error if this pattern is reached.
|
/// 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)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
|
@ -66,6 +66,7 @@ fn walk_expr<V: Visitor>(visitor: &mut V, expr: &Expr) {
|
||||||
branches,
|
branches,
|
||||||
region: _,
|
region: _,
|
||||||
branches_cond_var: _,
|
branches_cond_var: _,
|
||||||
|
exhaustive: _,
|
||||||
} => {
|
} => {
|
||||||
walk_when(visitor, *cond_var, *expr_var, loc_cond, branches);
|
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;
|
} = clos;
|
||||||
|
|
||||||
arguments
|
arguments.iter().for_each(|(var, _exhaustive_mark, arg)| {
|
||||||
.iter()
|
visitor.visit_pattern(&arg.value, arg.region, Some(*var))
|
||||||
.for_each(|(var, arg)| visitor.visit_pattern(&arg.value, arg.region, Some(*var)));
|
});
|
||||||
|
|
||||||
visitor.visit_expr(&loc_body.value, loc_body.region, *return_type);
|
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,
|
patterns,
|
||||||
value,
|
value,
|
||||||
guard,
|
guard,
|
||||||
|
redundant: _,
|
||||||
} = branch;
|
} = branch;
|
||||||
|
|
||||||
patterns
|
patterns
|
||||||
|
|
|
@ -5,11 +5,11 @@ use crate::pattern::{constrain_pattern, PatternState};
|
||||||
use roc_can::annotation::IntroducedVariables;
|
use roc_can::annotation::IntroducedVariables;
|
||||||
use roc_can::constraint::{Constraint, Constraints};
|
use roc_can::constraint::{Constraint, Constraints};
|
||||||
use roc_can::def::{Declaration, Def};
|
use roc_can::def::{Declaration, Def};
|
||||||
use roc_can::exhaustive::{sketch_rows, ExhaustiveContext};
|
use roc_can::exhaustive::{sketch_pattern_to_rows, sketch_when_branches, ExhaustiveContext};
|
||||||
use roc_can::expected::Expected::{self, *};
|
use roc_can::expected::Expected::{self, *};
|
||||||
use roc_can::expected::PExpected;
|
use roc_can::expected::PExpected;
|
||||||
use roc_can::expr::Expr::{self, *};
|
use roc_can::expr::Expr::{self, *};
|
||||||
use roc_can::expr::{AccessorData, ClosureData, Field, WhenBranch};
|
use roc_can::expr::{AccessorData, AnnotatedMark, ClosureData, Field, WhenBranch};
|
||||||
use roc_can::pattern::Pattern;
|
use roc_can::pattern::Pattern;
|
||||||
use roc_collections::all::{HumanIndex, MutMap, SendMap};
|
use roc_collections::all::{HumanIndex, MutMap, SendMap};
|
||||||
use roc_module::ident::{Lowercase, TagName};
|
use roc_module::ident::{Lowercase, TagName};
|
||||||
|
@ -50,7 +50,7 @@ pub struct Env {
|
||||||
fn constrain_untyped_args(
|
fn constrain_untyped_args(
|
||||||
constraints: &mut Constraints,
|
constraints: &mut Constraints,
|
||||||
env: &Env,
|
env: &Env,
|
||||||
arguments: &[(Variable, Loc<Pattern>)],
|
arguments: &[(Variable, AnnotatedMark, Loc<Pattern>)],
|
||||||
closure_type: Type,
|
closure_type: Type,
|
||||||
return_type: Type,
|
return_type: Type,
|
||||||
) -> (Vec<Variable>, PatternState, Type) {
|
) -> (Vec<Variable>, PatternState, Type) {
|
||||||
|
@ -59,7 +59,10 @@ fn constrain_untyped_args(
|
||||||
|
|
||||||
let mut pattern_state = PatternState::default();
|
let mut pattern_state = PatternState::default();
|
||||||
|
|
||||||
for (pattern_var, loc_pattern) in arguments {
|
for (pattern_var, annotated_mark, loc_pattern) in arguments {
|
||||||
|
// Untyped args don't need exhaustiveness checking because they are the source of truth!
|
||||||
|
let _ = annotated_mark;
|
||||||
|
|
||||||
let pattern_type = Type::Variable(*pattern_var);
|
let pattern_type = Type::Variable(*pattern_var);
|
||||||
let pattern_expected = PExpected::NoExpectation(pattern_type.clone());
|
let pattern_expected = PExpected::NoExpectation(pattern_type.clone());
|
||||||
|
|
||||||
|
@ -585,6 +588,7 @@ pub fn constrain_expr(
|
||||||
loc_cond,
|
loc_cond,
|
||||||
branches,
|
branches,
|
||||||
branches_cond_var,
|
branches_cond_var,
|
||||||
|
exhaustive,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let branches_cond_var = *branches_cond_var;
|
let branches_cond_var = *branches_cond_var;
|
||||||
|
@ -650,7 +654,7 @@ pub fn constrain_expr(
|
||||||
// constraints.
|
// constraints.
|
||||||
let mut pattern_vars = Vec::with_capacity(branches.len());
|
let mut pattern_vars = Vec::with_capacity(branches.len());
|
||||||
let mut pattern_headers = SendMap::default();
|
let mut pattern_headers = SendMap::default();
|
||||||
let mut pattern_cons = Vec::with_capacity(branches.len() + 1);
|
let mut pattern_cons = Vec::with_capacity(branches.len() + 2);
|
||||||
let mut branch_cons = Vec::with_capacity(branches.len());
|
let mut branch_cons = Vec::with_capacity(branches.len());
|
||||||
|
|
||||||
for (index, when_branch) in branches.iter().enumerate() {
|
for (index, when_branch) in branches.iter().enumerate() {
|
||||||
|
@ -719,14 +723,17 @@ pub fn constrain_expr(
|
||||||
pattern_cons.push(cond_constraint);
|
pattern_cons.push(cond_constraint);
|
||||||
|
|
||||||
// Now check the condition against the type expected by the branches.
|
// Now check the condition against the type expected by the branches.
|
||||||
let sketched_rows = sketch_rows(real_cond_var, branches_region, branches);
|
let sketched_rows = sketch_when_branches(real_cond_var, branches_region, branches);
|
||||||
let cond_matches_branches_constraint = constraints.exhaustive(
|
let cond_matches_branches_constraint = constraints.exhaustive(
|
||||||
real_cond_var,
|
real_cond_var,
|
||||||
loc_cond.region,
|
loc_cond.region,
|
||||||
loc_cond.value.category(),
|
Ok((
|
||||||
Expected::ForReason(Reason::WhenBranches, branches_cond_type, branches_region),
|
loc_cond.value.category(),
|
||||||
|
Expected::ForReason(Reason::WhenBranches, branches_cond_type, branches_region),
|
||||||
|
)),
|
||||||
sketched_rows,
|
sketched_rows,
|
||||||
ExhaustiveContext::BadCase,
|
ExhaustiveContext::BadCase,
|
||||||
|
*exhaustive,
|
||||||
);
|
);
|
||||||
pattern_cons.push(cond_matches_branches_constraint);
|
pattern_cons.push(cond_matches_branches_constraint);
|
||||||
|
|
||||||
|
@ -749,7 +756,12 @@ pub fn constrain_expr(
|
||||||
let branch_constraints = constraints.and_constraint(total_cons);
|
let branch_constraints = constraints.and_constraint(total_cons);
|
||||||
|
|
||||||
constraints.exists(
|
constraints.exists(
|
||||||
[branches_cond_var, real_cond_var, *expr_var],
|
[
|
||||||
|
exhaustive.variable_for_introduction(),
|
||||||
|
branches_cond_var,
|
||||||
|
real_cond_var,
|
||||||
|
*expr_var,
|
||||||
|
],
|
||||||
branch_constraints,
|
branch_constraints,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1166,8 +1178,8 @@ fn constrain_when_branch_help(
|
||||||
|
|
||||||
let mut state = PatternState {
|
let mut state = PatternState {
|
||||||
headers: SendMap::default(),
|
headers: SendMap::default(),
|
||||||
vars: Vec::with_capacity(1),
|
vars: Vec::with_capacity(2),
|
||||||
constraints: Vec::with_capacity(1),
|
constraints: Vec::with_capacity(2),
|
||||||
delayed_is_open_constraints: Vec::new(),
|
delayed_is_open_constraints: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1536,7 +1548,7 @@ fn constrain_typed_function_arguments(
|
||||||
def: &Def,
|
def: &Def,
|
||||||
def_pattern_state: &mut PatternState,
|
def_pattern_state: &mut PatternState,
|
||||||
argument_pattern_state: &mut PatternState,
|
argument_pattern_state: &mut PatternState,
|
||||||
arguments: &[(Variable, Loc<Pattern>)],
|
arguments: &[(Variable, AnnotatedMark, Loc<Pattern>)],
|
||||||
arg_types: &[Type],
|
arg_types: &[Type],
|
||||||
) {
|
) {
|
||||||
// ensure type matches the one in the annotation
|
// ensure type matches the one in the annotation
|
||||||
|
@ -1547,38 +1559,113 @@ fn constrain_typed_function_arguments(
|
||||||
};
|
};
|
||||||
|
|
||||||
let it = arguments.iter().zip(arg_types.iter()).enumerate();
|
let it = arguments.iter().zip(arg_types.iter()).enumerate();
|
||||||
for (index, ((pattern_var, loc_pattern), loc_ann)) in it {
|
for (index, ((pattern_var, annotated_mark, loc_pattern), ann)) in it {
|
||||||
let pattern_expected = PExpected::ForReason(
|
if loc_pattern.value.surely_exhaustive() {
|
||||||
PReason::TypedArg {
|
// OPT: we don't need to perform any type-level exhaustiveness checking.
|
||||||
index: HumanIndex::zero_based(index),
|
// Check instead only that the pattern unifies with the annotation type.
|
||||||
opt_name: opt_label,
|
let pattern_expected = PExpected::ForReason(
|
||||||
},
|
PReason::TypedArg {
|
||||||
loc_ann.clone(),
|
index: HumanIndex::zero_based(index),
|
||||||
loc_pattern.region,
|
opt_name: opt_label,
|
||||||
);
|
},
|
||||||
|
ann.clone(),
|
||||||
constrain_pattern(
|
|
||||||
constraints,
|
|
||||||
env,
|
|
||||||
&loc_pattern.value,
|
|
||||||
loc_pattern.region,
|
|
||||||
pattern_expected,
|
|
||||||
argument_pattern_state,
|
|
||||||
);
|
|
||||||
|
|
||||||
{
|
|
||||||
// NOTE: because we perform an equality with part of the signature
|
|
||||||
// this constraint must be to the def_pattern_state's constraints
|
|
||||||
def_pattern_state.vars.push(*pattern_var);
|
|
||||||
|
|
||||||
let pattern_con = constraints.equal_types_var(
|
|
||||||
*pattern_var,
|
|
||||||
Expected::NoExpectation(loc_ann.clone()),
|
|
||||||
Category::Storage(std::file!(), std::line!()),
|
|
||||||
loc_pattern.region,
|
loc_pattern.region,
|
||||||
);
|
);
|
||||||
|
|
||||||
def_pattern_state.constraints.push(pattern_con);
|
constrain_pattern(
|
||||||
|
constraints,
|
||||||
|
env,
|
||||||
|
&loc_pattern.value,
|
||||||
|
loc_pattern.region,
|
||||||
|
pattern_expected,
|
||||||
|
argument_pattern_state,
|
||||||
|
);
|
||||||
|
|
||||||
|
{
|
||||||
|
// NOTE: because we perform an equality with part of the signature
|
||||||
|
// this constraint must be to the def_pattern_state's constraints
|
||||||
|
def_pattern_state.vars.push(*pattern_var);
|
||||||
|
|
||||||
|
let pattern_con = constraints.equal_types_var(
|
||||||
|
*pattern_var,
|
||||||
|
Expected::NoExpectation(ann.clone()),
|
||||||
|
Category::Storage(std::file!(), std::line!()),
|
||||||
|
loc_pattern.region,
|
||||||
|
);
|
||||||
|
|
||||||
|
def_pattern_state.constraints.push(pattern_con);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We need to check the types, and run exhaustiveness checking.
|
||||||
|
let &AnnotatedMark {
|
||||||
|
annotation_var,
|
||||||
|
exhaustive,
|
||||||
|
} = annotated_mark;
|
||||||
|
|
||||||
|
def_pattern_state.vars.push(*pattern_var);
|
||||||
|
def_pattern_state.vars.push(annotation_var);
|
||||||
|
|
||||||
|
{
|
||||||
|
// First, solve the type that the pattern is expecting to match in this
|
||||||
|
// position.
|
||||||
|
let pattern_expected = PExpected::NoExpectation(Type::Variable(*pattern_var));
|
||||||
|
constrain_pattern(
|
||||||
|
constraints,
|
||||||
|
env,
|
||||||
|
&loc_pattern.value,
|
||||||
|
loc_pattern.region,
|
||||||
|
pattern_expected,
|
||||||
|
argument_pattern_state,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Store the actual type in a variable.
|
||||||
|
argument_pattern_state
|
||||||
|
.constraints
|
||||||
|
.push(constraints.equal_types_var(
|
||||||
|
annotation_var,
|
||||||
|
Expected::NoExpectation(ann.clone()),
|
||||||
|
Category::Storage(file!(), line!()),
|
||||||
|
Region::zero(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// let pattern_expected = PExpected::ForReason(
|
||||||
|
// PReason::TypedArg {
|
||||||
|
// index: HumanIndex::zero_based(index),
|
||||||
|
// opt_name: opt_label,
|
||||||
|
// },
|
||||||
|
// ann.clone(),
|
||||||
|
// loc_pattern.region,
|
||||||
|
// );
|
||||||
|
|
||||||
|
// Exhaustiveness-check the type in the pattern against what the
|
||||||
|
// annotation wants.
|
||||||
|
let sketched_rows =
|
||||||
|
sketch_pattern_to_rows(annotation_var, loc_pattern.region, &loc_pattern.value);
|
||||||
|
let category = loc_pattern.value.category();
|
||||||
|
let expected = PExpected::ForReason(
|
||||||
|
PReason::TypedArg {
|
||||||
|
index: HumanIndex::zero_based(index),
|
||||||
|
opt_name: opt_label,
|
||||||
|
},
|
||||||
|
Type::Variable(*pattern_var),
|
||||||
|
loc_pattern.region,
|
||||||
|
);
|
||||||
|
let exhaustive_constraint = constraints.exhaustive(
|
||||||
|
annotation_var,
|
||||||
|
loc_pattern.region,
|
||||||
|
Err((category, expected)),
|
||||||
|
sketched_rows,
|
||||||
|
ExhaustiveContext::BadArg,
|
||||||
|
exhaustive,
|
||||||
|
);
|
||||||
|
argument_pattern_state
|
||||||
|
.constraints
|
||||||
|
.push(exhaustive_constraint)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1883,7 +1970,6 @@ pub fn rec_defs_help(
|
||||||
delayed_is_open_constraints: vec![],
|
delayed_is_open_constraints: vec![],
|
||||||
};
|
};
|
||||||
let mut vars = Vec::with_capacity(state.vars.capacity() + 1);
|
let mut vars = Vec::with_capacity(state.vars.capacity() + 1);
|
||||||
let mut pattern_types = Vec::with_capacity(state.vars.capacity());
|
|
||||||
let ret_var = *ret_var;
|
let ret_var = *ret_var;
|
||||||
let closure_var = *closure_var;
|
let closure_var = *closure_var;
|
||||||
let closure_ext_var = *closure_ext_var;
|
let closure_ext_var = *closure_ext_var;
|
||||||
|
@ -1893,53 +1979,16 @@ pub fn rec_defs_help(
|
||||||
vars.push(closure_var);
|
vars.push(closure_var);
|
||||||
vars.push(closure_ext_var);
|
vars.push(closure_ext_var);
|
||||||
|
|
||||||
let it = arguments.iter().zip(arg_types.iter()).enumerate();
|
constrain_typed_function_arguments(
|
||||||
for (index, ((pattern_var, loc_pattern), loc_ann)) in it {
|
constraints,
|
||||||
{
|
env,
|
||||||
// ensure type matches the one in the annotation
|
def,
|
||||||
let opt_label =
|
&mut def_pattern_state,
|
||||||
if let Pattern::Identifier(label) = def.loc_pattern.value {
|
&mut state,
|
||||||
Some(label)
|
arguments,
|
||||||
} else {
|
arg_types,
|
||||||
None
|
);
|
||||||
};
|
let pattern_types = arguments.iter().map(|a| Type::Variable(a.0)).collect();
|
||||||
let pattern_type: &Type = loc_ann;
|
|
||||||
|
|
||||||
let pattern_expected = PExpected::ForReason(
|
|
||||||
PReason::TypedArg {
|
|
||||||
index: HumanIndex::zero_based(index),
|
|
||||||
opt_name: opt_label,
|
|
||||||
},
|
|
||||||
pattern_type.clone(),
|
|
||||||
loc_pattern.region,
|
|
||||||
);
|
|
||||||
|
|
||||||
constrain_pattern(
|
|
||||||
constraints,
|
|
||||||
env,
|
|
||||||
&loc_pattern.value,
|
|
||||||
loc_pattern.region,
|
|
||||||
pattern_expected,
|
|
||||||
&mut state,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// NOTE: because we perform an equality with part of the signature
|
|
||||||
// this constraint must be to the def_pattern_state's constraints
|
|
||||||
def_pattern_state.vars.push(*pattern_var);
|
|
||||||
pattern_types.push(Type::Variable(*pattern_var));
|
|
||||||
|
|
||||||
let pattern_con = constraints.equal_types_var(
|
|
||||||
*pattern_var,
|
|
||||||
Expected::NoExpectation(loc_ann.clone()),
|
|
||||||
Category::Storage(std::file!(), std::line!()),
|
|
||||||
loc_pattern.region,
|
|
||||||
);
|
|
||||||
|
|
||||||
def_pattern_state.constraints.push(pattern_con);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let closure_constraint = constrain_closure_size(
|
let closure_constraint = constrain_closure_size(
|
||||||
constraints,
|
constraints,
|
||||||
|
|
|
@ -519,7 +519,6 @@ pub struct MonomorphizedModule<'a> {
|
||||||
pub platform_path: Box<str>,
|
pub platform_path: Box<str>,
|
||||||
pub can_problems: MutMap<ModuleId, Vec<roc_problem::can::Problem>>,
|
pub can_problems: MutMap<ModuleId, Vec<roc_problem::can::Problem>>,
|
||||||
pub type_problems: MutMap<ModuleId, Vec<solve::TypeError>>,
|
pub type_problems: MutMap<ModuleId, Vec<solve::TypeError>>,
|
||||||
pub mono_problems: MutMap<ModuleId, Vec<roc_mono::ir::MonoProblem>>,
|
|
||||||
pub procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
pub procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
||||||
pub entry_point: EntryPoint<'a>,
|
pub entry_point: EntryPoint<'a>,
|
||||||
pub exposed_to_host: ExposedToHost,
|
pub exposed_to_host: ExposedToHost,
|
||||||
|
@ -547,10 +546,6 @@ impl<'a> MonomorphizedModule<'a> {
|
||||||
total += problems.len();
|
total += problems.len();
|
||||||
}
|
}
|
||||||
|
|
||||||
for problems in self.mono_problems.values() {
|
|
||||||
total += problems.len();
|
|
||||||
}
|
|
||||||
|
|
||||||
total
|
total
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2321,7 +2316,6 @@ fn finish_specialization(
|
||||||
} = state;
|
} = state;
|
||||||
|
|
||||||
let ModuleCache {
|
let ModuleCache {
|
||||||
mono_problems,
|
|
||||||
type_problems,
|
type_problems,
|
||||||
can_problems,
|
can_problems,
|
||||||
sources,
|
sources,
|
||||||
|
@ -2384,7 +2378,6 @@ fn finish_specialization(
|
||||||
|
|
||||||
Ok(MonomorphizedModule {
|
Ok(MonomorphizedModule {
|
||||||
can_problems,
|
can_problems,
|
||||||
mono_problems,
|
|
||||||
type_problems,
|
type_problems,
|
||||||
output_path: output_path.unwrap_or(DEFAULT_APP_OUTPUT_PATH).into(),
|
output_path: output_path.unwrap_or(DEFAULT_APP_OUTPUT_PATH).into(),
|
||||||
platform_path,
|
platform_path,
|
||||||
|
@ -4291,7 +4284,6 @@ fn add_def_to_module<'a>(
|
||||||
|
|
||||||
let partial_proc = PartialProc::from_named_function(
|
let partial_proc = PartialProc::from_named_function(
|
||||||
mono_env,
|
mono_env,
|
||||||
layout_cache,
|
|
||||||
annotation,
|
annotation,
|
||||||
loc_args,
|
loc_args,
|
||||||
*loc_body,
|
*loc_body,
|
||||||
|
|
|
@ -8,7 +8,7 @@ use bumpalo::collections::Vec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||||
use roc_can::abilities::AbilitiesStore;
|
use roc_can::abilities::AbilitiesStore;
|
||||||
use roc_can::expr::{ClosureData, IntValue};
|
use roc_can::expr::{AnnotatedMark, ClosureData, IntValue};
|
||||||
use roc_collections::all::{default_hasher, BumpMap, BumpMapDefault, MutMap};
|
use roc_collections::all::{default_hasher, BumpMap, BumpMapDefault, MutMap};
|
||||||
use roc_exhaustive::{Ctor, CtorName, Guard, RenderAs, TagId};
|
use roc_exhaustive::{Ctor, CtorName, Guard, RenderAs, TagId};
|
||||||
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
||||||
|
@ -18,7 +18,10 @@ use roc_problem::can::{RuntimeError, ShadowKind};
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_std::RocDec;
|
use roc_std::RocDec;
|
||||||
use roc_target::TargetInfo;
|
use roc_target::TargetInfo;
|
||||||
use roc_types::subs::{Content, FlatType, StorageSubs, Subs, Variable, VariableSubsSlice};
|
use roc_types::subs::{
|
||||||
|
Content, ExhaustiveMark, FlatType, RedundantMark, StorageSubs, Subs, Variable,
|
||||||
|
VariableSubsSlice,
|
||||||
|
};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder};
|
use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder};
|
||||||
|
|
||||||
|
@ -204,9 +207,8 @@ impl<'a> PartialProc<'a> {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn from_named_function(
|
pub fn from_named_function(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
layout_cache: &mut LayoutCache<'a>,
|
|
||||||
annotation: Variable,
|
annotation: Variable,
|
||||||
loc_args: std::vec::Vec<(Variable, Loc<roc_can::pattern::Pattern>)>,
|
loc_args: std::vec::Vec<(Variable, AnnotatedMark, Loc<roc_can::pattern::Pattern>)>,
|
||||||
loc_body: Loc<roc_can::expr::Expr>,
|
loc_body: Loc<roc_can::expr::Expr>,
|
||||||
captured_symbols: CapturedSymbols<'a>,
|
captured_symbols: CapturedSymbols<'a>,
|
||||||
is_self_recursive: bool,
|
is_self_recursive: bool,
|
||||||
|
@ -214,7 +216,7 @@ impl<'a> PartialProc<'a> {
|
||||||
) -> PartialProc<'a> {
|
) -> PartialProc<'a> {
|
||||||
let number_of_arguments = loc_args.len();
|
let number_of_arguments = loc_args.len();
|
||||||
|
|
||||||
match patterns_to_when(env, layout_cache, loc_args, ret_var, loc_body) {
|
match patterns_to_when(env, loc_args, ret_var, loc_body) {
|
||||||
Ok((_, pattern_symbols, body)) => {
|
Ok((_, pattern_symbols, body)) => {
|
||||||
// a named closure. Since these aren't specialized by the surrounding
|
// a named closure. Since these aren't specialized by the surrounding
|
||||||
// context, we can't add pending specializations for them yet.
|
// context, we can't add pending specializations for them yet.
|
||||||
|
@ -855,7 +857,7 @@ impl<'a> Procs<'a> {
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
symbol: Symbol,
|
symbol: Symbol,
|
||||||
annotation: Variable,
|
annotation: Variable,
|
||||||
loc_args: std::vec::Vec<(Variable, Loc<roc_can::pattern::Pattern>)>,
|
loc_args: std::vec::Vec<(Variable, AnnotatedMark, Loc<roc_can::pattern::Pattern>)>,
|
||||||
loc_body: Loc<roc_can::expr::Expr>,
|
loc_body: Loc<roc_can::expr::Expr>,
|
||||||
captured_symbols: CapturedSymbols<'a>,
|
captured_symbols: CapturedSymbols<'a>,
|
||||||
ret_var: Variable,
|
ret_var: Variable,
|
||||||
|
@ -874,7 +876,7 @@ impl<'a> Procs<'a> {
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
match patterns_to_when(env, layout_cache, loc_args, ret_var, loc_body) {
|
match patterns_to_when(env, loc_args, ret_var, loc_body) {
|
||||||
Ok((_, pattern_symbols, body)) => {
|
Ok((_, pattern_symbols, body)) => {
|
||||||
// an anonymous closure. These will always be specialized already
|
// an anonymous closure. These will always be specialized already
|
||||||
// by the surrounding context, so we can add pending specializations
|
// by the surrounding context, so we can add pending specializations
|
||||||
|
@ -1935,8 +1937,7 @@ impl<'a> Stmt<'a> {
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
fn patterns_to_when<'a>(
|
fn patterns_to_when<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
layout_cache: &mut LayoutCache<'a>,
|
patterns: std::vec::Vec<(Variable, AnnotatedMark, Loc<roc_can::pattern::Pattern>)>,
|
||||||
patterns: std::vec::Vec<(Variable, Loc<roc_can::pattern::Pattern>)>,
|
|
||||||
body_var: Variable,
|
body_var: Variable,
|
||||||
body: Loc<roc_can::expr::Expr>,
|
body: Loc<roc_can::expr::Expr>,
|
||||||
) -> Result<(Vec<'a, Variable>, Vec<'a, Symbol>, Loc<roc_can::expr::Expr>), Loc<RuntimeError>> {
|
) -> Result<(Vec<'a, Variable>, Vec<'a, Symbol>, Loc<roc_can::expr::Expr>), Loc<RuntimeError>> {
|
||||||
|
@ -1951,66 +1952,26 @@ fn patterns_to_when<'a>(
|
||||||
// NOTE this fails if the pattern contains rigid variables,
|
// NOTE this fails if the pattern contains rigid variables,
|
||||||
// see https://github.com/rtfeldman/roc/issues/786
|
// see https://github.com/rtfeldman/roc/issues/786
|
||||||
// this must be fixed when moving exhaustiveness checking to the new canonical AST
|
// this must be fixed when moving exhaustiveness checking to the new canonical AST
|
||||||
for (pattern_var, pattern) in patterns.into_iter() {
|
for (pattern_var, annotated_mark, pattern) in patterns.into_iter() {
|
||||||
let context = roc_exhaustive::Context::BadArg;
|
if annotated_mark.exhaustive.is_non_exhaustive(env.subs) {
|
||||||
let mono_pattern = match from_can_pattern(env, layout_cache, &pattern.value) {
|
// Even if the body was Ok, replace it with this Err.
|
||||||
Ok((pat, _assignments)) => {
|
// If it was already an Err, leave it at that Err, so the first
|
||||||
// Don't apply any assignments (e.g. to initialize optional variables) yet.
|
// RuntimeError we encountered remains the first.
|
||||||
// We'll take care of that later when expanding the new "when" branch.
|
let value = RuntimeError::UnsupportedPattern(pattern.region);
|
||||||
pat
|
body = body.and({
|
||||||
}
|
Err(Loc {
|
||||||
Err(runtime_error) => {
|
region: pattern.region,
|
||||||
// Even if the body was Ok, replace it with this Err.
|
value,
|
||||||
// If it was already an Err, leave it at that Err, so the first
|
})
|
||||||
// RuntimeError we encountered remains the first.
|
});
|
||||||
body = body.and({
|
} else if let Ok(unwrapped_body) = body {
|
||||||
Err(Loc {
|
let (new_symbol, new_body) =
|
||||||
region: pattern.region,
|
pattern_to_when(env, pattern_var, pattern, body_var, unwrapped_body);
|
||||||
value: runtime_error,
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
continue;
|
symbols.push(new_symbol);
|
||||||
}
|
arg_vars.push(pattern_var);
|
||||||
};
|
|
||||||
|
|
||||||
match crate::exhaustive::check(
|
body = Ok(new_body)
|
||||||
pattern.region,
|
|
||||||
&[(
|
|
||||||
Loc::at(pattern.region, mono_pattern),
|
|
||||||
roc_exhaustive::Guard::NoGuard,
|
|
||||||
)],
|
|
||||||
context,
|
|
||||||
) {
|
|
||||||
Ok(_) => {
|
|
||||||
// Replace the body with a new one, but only if it was Ok.
|
|
||||||
if let Ok(unwrapped_body) = body {
|
|
||||||
let (new_symbol, new_body) =
|
|
||||||
pattern_to_when(env, pattern_var, pattern, body_var, unwrapped_body);
|
|
||||||
|
|
||||||
symbols.push(new_symbol);
|
|
||||||
arg_vars.push(pattern_var);
|
|
||||||
|
|
||||||
body = Ok(new_body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(errors) => {
|
|
||||||
for error in errors {
|
|
||||||
env.problems.push(MonoProblem::PatternProblem(error))
|
|
||||||
}
|
|
||||||
|
|
||||||
let value = RuntimeError::UnsupportedPattern(pattern.region);
|
|
||||||
|
|
||||||
// Even if the body was Ok, replace it with this Err.
|
|
||||||
// If it was already an Err, leave it at that Err, so the first
|
|
||||||
// RuntimeError we encountered remains the first.
|
|
||||||
body = body.and({
|
|
||||||
Err(Loc {
|
|
||||||
region: pattern.region,
|
|
||||||
value,
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2090,8 +2051,12 @@ fn pattern_to_when<'a>(
|
||||||
patterns: vec![pattern],
|
patterns: vec![pattern],
|
||||||
value: body,
|
value: body,
|
||||||
guard: None,
|
guard: None,
|
||||||
|
// If this type-checked, it's non-redundant
|
||||||
|
redundant: RedundantMark::known_non_redundant(),
|
||||||
}],
|
}],
|
||||||
branches_cond_var: pattern_var,
|
branches_cond_var: pattern_var,
|
||||||
|
// If this type-checked, it's exhaustive
|
||||||
|
exhaustive: ExhaustiveMark::known_exhaustive(),
|
||||||
};
|
};
|
||||||
|
|
||||||
(symbol, Loc::at_zero(wrapped_body))
|
(symbol, Loc::at_zero(wrapped_body))
|
||||||
|
@ -3259,7 +3224,7 @@ pub fn with_hole<'a>(
|
||||||
LetNonRec(def, cont, _) => {
|
LetNonRec(def, cont, _) => {
|
||||||
if let roc_can::pattern::Pattern::Identifier(symbol) = def.loc_pattern.value {
|
if let roc_can::pattern::Pattern::Identifier(symbol) = def.loc_pattern.value {
|
||||||
if let Closure(closure_data) = def.loc_expr.value {
|
if let Closure(closure_data) = def.loc_expr.value {
|
||||||
register_noncapturing_closure(env, procs, layout_cache, symbol, closure_data);
|
register_noncapturing_closure(env, procs, symbol, closure_data);
|
||||||
|
|
||||||
return with_hole(
|
return with_hole(
|
||||||
env,
|
env,
|
||||||
|
@ -3345,24 +3310,6 @@ pub fn with_hole<'a>(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let context = roc_exhaustive::Context::BadDestruct;
|
|
||||||
match crate::exhaustive::check(
|
|
||||||
def.loc_pattern.region,
|
|
||||||
&[(
|
|
||||||
Loc::at(def.loc_pattern.region, mono_pattern.clone()),
|
|
||||||
roc_exhaustive::Guard::NoGuard,
|
|
||||||
)],
|
|
||||||
context,
|
|
||||||
) {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(errors) => {
|
|
||||||
for error in errors {
|
|
||||||
env.problems.push(MonoProblem::PatternProblem(error))
|
|
||||||
}
|
|
||||||
} // TODO make all variables bound in the pattern evaluate to a runtime error
|
|
||||||
// return Stmt::RuntimeError("TODO non-exhaustive pattern");
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut hole = hole;
|
let mut hole = hole;
|
||||||
|
|
||||||
for (symbol, variable, expr) in assignments {
|
for (symbol, variable, expr) in assignments {
|
||||||
|
@ -3402,13 +3349,7 @@ pub fn with_hole<'a>(
|
||||||
for def in defs.into_iter() {
|
for def in defs.into_iter() {
|
||||||
if let roc_can::pattern::Pattern::Identifier(symbol) = &def.loc_pattern.value {
|
if let roc_can::pattern::Pattern::Identifier(symbol) = &def.loc_pattern.value {
|
||||||
if let Closure(closure_data) = def.loc_expr.value {
|
if let Closure(closure_data) = def.loc_expr.value {
|
||||||
register_noncapturing_closure(
|
register_noncapturing_closure(env, procs, *symbol, closure_data);
|
||||||
env,
|
|
||||||
procs,
|
|
||||||
layout_cache,
|
|
||||||
*symbol,
|
|
||||||
closure_data,
|
|
||||||
);
|
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -3751,10 +3692,11 @@ pub fn with_hole<'a>(
|
||||||
When {
|
When {
|
||||||
cond_var,
|
cond_var,
|
||||||
expr_var,
|
expr_var,
|
||||||
region,
|
region: _,
|
||||||
loc_cond,
|
loc_cond,
|
||||||
branches,
|
branches,
|
||||||
branches_cond_var: _,
|
branches_cond_var: _,
|
||||||
|
exhaustive,
|
||||||
} => {
|
} => {
|
||||||
let cond_symbol = possible_reuse_symbol(env, procs, &loc_cond.value);
|
let cond_symbol = possible_reuse_symbol(env, procs, &loc_cond.value);
|
||||||
|
|
||||||
|
@ -3764,9 +3706,9 @@ pub fn with_hole<'a>(
|
||||||
env,
|
env,
|
||||||
cond_var,
|
cond_var,
|
||||||
expr_var,
|
expr_var,
|
||||||
region,
|
|
||||||
cond_symbol,
|
cond_symbol,
|
||||||
branches,
|
branches,
|
||||||
|
exhaustive,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
procs,
|
procs,
|
||||||
Some(id),
|
Some(id),
|
||||||
|
@ -5176,7 +5118,7 @@ fn tag_union_to_function<'a>(
|
||||||
|
|
||||||
let loc_expr = Loc::at_zero(roc_can::expr::Expr::Var(arg_symbol));
|
let loc_expr = Loc::at_zero(roc_can::expr::Expr::Var(arg_symbol));
|
||||||
|
|
||||||
loc_pattern_args.push((arg_var, loc_pattern));
|
loc_pattern_args.push((arg_var, AnnotatedMark::known_exhaustive(), loc_pattern));
|
||||||
loc_expr_args.push((arg_var, loc_expr));
|
loc_expr_args.push((arg_var, loc_expr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5278,7 +5220,6 @@ fn sorted_field_symbols<'a>(
|
||||||
fn register_noncapturing_closure<'a>(
|
fn register_noncapturing_closure<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
procs: &mut Procs<'a>,
|
procs: &mut Procs<'a>,
|
||||||
layout_cache: &mut LayoutCache<'a>,
|
|
||||||
closure_name: Symbol,
|
closure_name: Symbol,
|
||||||
closure_data: ClosureData,
|
closure_data: ClosureData,
|
||||||
) {
|
) {
|
||||||
|
@ -5306,7 +5247,6 @@ fn register_noncapturing_closure<'a>(
|
||||||
|
|
||||||
let partial_proc = PartialProc::from_named_function(
|
let partial_proc = PartialProc::from_named_function(
|
||||||
env,
|
env,
|
||||||
layout_cache,
|
|
||||||
function_type,
|
function_type,
|
||||||
arguments,
|
arguments,
|
||||||
loc_body,
|
loc_body,
|
||||||
|
@ -5390,7 +5330,6 @@ fn register_capturing_closure<'a>(
|
||||||
|
|
||||||
let partial_proc = PartialProc::from_named_function(
|
let partial_proc = PartialProc::from_named_function(
|
||||||
env,
|
env,
|
||||||
layout_cache,
|
|
||||||
function_type,
|
function_type,
|
||||||
arguments,
|
arguments,
|
||||||
loc_body,
|
loc_body,
|
||||||
|
@ -5442,10 +5381,11 @@ pub fn from_can<'a>(
|
||||||
When {
|
When {
|
||||||
cond_var,
|
cond_var,
|
||||||
expr_var,
|
expr_var,
|
||||||
region,
|
region: _,
|
||||||
loc_cond,
|
loc_cond,
|
||||||
branches,
|
branches,
|
||||||
branches_cond_var: _,
|
branches_cond_var: _,
|
||||||
|
exhaustive,
|
||||||
} => {
|
} => {
|
||||||
let cond_symbol = possible_reuse_symbol(env, procs, &loc_cond.value);
|
let cond_symbol = possible_reuse_symbol(env, procs, &loc_cond.value);
|
||||||
|
|
||||||
|
@ -5453,9 +5393,9 @@ pub fn from_can<'a>(
|
||||||
env,
|
env,
|
||||||
cond_var,
|
cond_var,
|
||||||
expr_var,
|
expr_var,
|
||||||
region,
|
|
||||||
cond_symbol,
|
cond_symbol,
|
||||||
branches,
|
branches,
|
||||||
|
exhaustive,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
procs,
|
procs,
|
||||||
None,
|
None,
|
||||||
|
@ -5581,7 +5521,6 @@ pub fn from_can<'a>(
|
||||||
register_noncapturing_closure(
|
register_noncapturing_closure(
|
||||||
env,
|
env,
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
|
||||||
*symbol,
|
*symbol,
|
||||||
accessor_data.to_closure_data(fresh_record_symbol),
|
accessor_data.to_closure_data(fresh_record_symbol),
|
||||||
);
|
);
|
||||||
|
@ -5763,25 +5702,6 @@ pub fn from_can<'a>(
|
||||||
hole,
|
hole,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let context = roc_exhaustive::Context::BadDestruct;
|
|
||||||
match crate::exhaustive::check(
|
|
||||||
def.loc_pattern.region,
|
|
||||||
&[(
|
|
||||||
Loc::at(def.loc_pattern.region, mono_pattern.clone()),
|
|
||||||
roc_exhaustive::Guard::NoGuard,
|
|
||||||
)],
|
|
||||||
context,
|
|
||||||
) {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(errors) => {
|
|
||||||
for error in errors {
|
|
||||||
env.problems.push(MonoProblem::PatternProblem(error))
|
|
||||||
}
|
|
||||||
|
|
||||||
return Stmt::RuntimeError("TODO non-exhaustive pattern");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert the continuation
|
// convert the continuation
|
||||||
let mut stmt = from_can(env, variable, cont.value, procs, layout_cache);
|
let mut stmt = from_can(env, variable, cont.value, procs, layout_cache);
|
||||||
|
|
||||||
|
@ -5822,8 +5742,8 @@ pub fn from_can<'a>(
|
||||||
|
|
||||||
fn to_opt_branches<'a>(
|
fn to_opt_branches<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
region: Region,
|
|
||||||
branches: std::vec::Vec<roc_can::expr::WhenBranch>,
|
branches: std::vec::Vec<roc_can::expr::WhenBranch>,
|
||||||
|
exhaustive_mark: ExhaustiveMark,
|
||||||
layout_cache: &mut LayoutCache<'a>,
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
) -> std::vec::Vec<(
|
) -> std::vec::Vec<(
|
||||||
Pattern<'a>,
|
Pattern<'a>,
|
||||||
|
@ -5842,6 +5762,11 @@ fn to_opt_branches<'a>(
|
||||||
Guard::NoGuard
|
Guard::NoGuard
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if when_branch.redundant.is_redundant(env.subs) {
|
||||||
|
// Don't codegen this branch since it's redundant.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for loc_pattern in when_branch.patterns {
|
for loc_pattern in when_branch.patterns {
|
||||||
match from_can_pattern(env, layout_cache, &loc_pattern.value) {
|
match from_can_pattern(env, layout_cache, &loc_pattern.value) {
|
||||||
Ok((mono_pattern, assignments)) => {
|
Ok((mono_pattern, assignments)) => {
|
||||||
|
@ -5891,45 +5816,14 @@ fn to_opt_branches<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE exhaustiveness is checked after the construction of all the branches
|
if exhaustive_mark.is_non_exhaustive(env.subs) {
|
||||||
// In contrast to elm (currently), we still do codegen even if a pattern is non-exhaustive.
|
// In contrast to elm (currently), we still do codegen even if a pattern is non-exhaustive.
|
||||||
// So we not only report exhaustiveness errors, but also correct them
|
// So we not only report exhaustiveness errors, but also correct them
|
||||||
let context = roc_exhaustive::Context::BadCase;
|
opt_branches.push((
|
||||||
match crate::exhaustive::check(region, &loc_branches, context) {
|
Pattern::Underscore,
|
||||||
Ok(_) => {}
|
None,
|
||||||
Err(errors) => {
|
roc_can::expr::Expr::RuntimeError(roc_problem::can::RuntimeError::NonExhaustivePattern),
|
||||||
use roc_exhaustive::Error::*;
|
));
|
||||||
let mut is_not_exhaustive = false;
|
|
||||||
let mut overlapping_branches = std::vec::Vec::new();
|
|
||||||
|
|
||||||
for error in errors {
|
|
||||||
match &error {
|
|
||||||
Incomplete(_, _, _) => {
|
|
||||||
is_not_exhaustive = true;
|
|
||||||
}
|
|
||||||
Redundant { index, .. } => {
|
|
||||||
overlapping_branches.push(index.to_zero_based());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
env.problems.push(MonoProblem::PatternProblem(error))
|
|
||||||
}
|
|
||||||
|
|
||||||
overlapping_branches.sort_unstable();
|
|
||||||
|
|
||||||
for i in overlapping_branches.into_iter().rev() {
|
|
||||||
opt_branches.remove(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
if is_not_exhaustive {
|
|
||||||
opt_branches.push((
|
|
||||||
Pattern::Underscore,
|
|
||||||
None,
|
|
||||||
roc_can::expr::Expr::RuntimeError(
|
|
||||||
roc_problem::can::RuntimeError::NonExhaustivePattern,
|
|
||||||
),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
opt_branches
|
opt_branches
|
||||||
|
@ -5940,9 +5834,9 @@ fn from_can_when<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
cond_var: Variable,
|
cond_var: Variable,
|
||||||
expr_var: Variable,
|
expr_var: Variable,
|
||||||
region: Region,
|
|
||||||
cond_symbol: Symbol,
|
cond_symbol: Symbol,
|
||||||
branches: std::vec::Vec<roc_can::expr::WhenBranch>,
|
branches: std::vec::Vec<roc_can::expr::WhenBranch>,
|
||||||
|
exhaustive_mark: ExhaustiveMark,
|
||||||
layout_cache: &mut LayoutCache<'a>,
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
procs: &mut Procs<'a>,
|
procs: &mut Procs<'a>,
|
||||||
join_point: Option<JoinPointId>,
|
join_point: Option<JoinPointId>,
|
||||||
|
@ -5952,7 +5846,7 @@ fn from_can_when<'a>(
|
||||||
// We can't know what to return!
|
// We can't know what to return!
|
||||||
return Stmt::RuntimeError("Hit a 0-branch when expression");
|
return Stmt::RuntimeError("Hit a 0-branch when expression");
|
||||||
}
|
}
|
||||||
let opt_branches = to_opt_branches(env, region, branches, layout_cache);
|
let opt_branches = to_opt_branches(env, branches, exhaustive_mark, layout_cache);
|
||||||
|
|
||||||
let cond_layout =
|
let cond_layout =
|
||||||
return_on_layout_error!(env, layout_cache.from_var(env.arena, cond_var, env.subs));
|
return_on_layout_error!(env, layout_cache.from_var(env.arena, cond_var, env.subs));
|
||||||
|
|
|
@ -1172,7 +1172,7 @@ fn solve(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&Exhaustive(eq, sketched_rows, context) => {
|
&Exhaustive(eq, sketched_rows, context, exhaustive_mark) => {
|
||||||
// A few cases:
|
// A few cases:
|
||||||
// 1. Either condition or branch types already have a type error. In this case just
|
// 1. Either condition or branch types already have a type error. In this case just
|
||||||
// propagate it.
|
// propagate it.
|
||||||
|
@ -1184,19 +1184,39 @@ fn solve(
|
||||||
// 4. Condition and branch types aren't "almost equal", this is just a normal type
|
// 4. Condition and branch types aren't "almost equal", this is just a normal type
|
||||||
// error.
|
// error.
|
||||||
|
|
||||||
let roc_can::constraint::Eq(
|
let (real_var, real_region, expected_type, category_and_expected) = match eq {
|
||||||
real_var,
|
Ok(eq) => {
|
||||||
expected_branches,
|
let roc_can::constraint::Eq(real_var, expected, category, real_region) =
|
||||||
real_category,
|
constraints.eq[eq.index()];
|
||||||
real_region,
|
let expected = &constraints.expectations[expected.index()];
|
||||||
) = constraints.eq[eq.index()];
|
(
|
||||||
|
real_var,
|
||||||
|
real_region,
|
||||||
|
expected.get_type_ref(),
|
||||||
|
Ok((category, expected)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Err(peq) => {
|
||||||
|
let roc_can::constraint::PatternEq(
|
||||||
|
real_var,
|
||||||
|
expected,
|
||||||
|
category,
|
||||||
|
real_region,
|
||||||
|
) = constraints.pattern_eq[peq.index()];
|
||||||
|
let expected = &constraints.pattern_expectations[expected.index()];
|
||||||
|
(
|
||||||
|
real_var,
|
||||||
|
real_region,
|
||||||
|
expected.get_type_ref(),
|
||||||
|
Err((category, expected)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let real_var =
|
let real_var =
|
||||||
either_type_index_to_var(constraints, subs, rank, pools, aliases, real_var);
|
either_type_index_to_var(constraints, subs, rank, pools, aliases, real_var);
|
||||||
|
|
||||||
let expected_branches = &constraints.expectations[expected_branches.index()];
|
let branches_var = type_to_var(subs, rank, pools, aliases, expected_type);
|
||||||
let branches_var =
|
|
||||||
type_to_var(subs, rank, pools, aliases, expected_branches.get_type_ref());
|
|
||||||
|
|
||||||
let real_content = subs.get_content_without_compacting(real_var);
|
let real_content = subs.get_content_without_compacting(real_var);
|
||||||
let branches_content = subs.get_content_without_compacting(branches_var);
|
let branches_content = subs.get_content_without_compacting(branches_var);
|
||||||
|
@ -1256,14 +1276,32 @@ fn solve(
|
||||||
Failure(vars, actual_type, expected_type, _bad_impls) => {
|
Failure(vars, actual_type, expected_type, _bad_impls) => {
|
||||||
introduce(subs, rank, pools, &vars);
|
introduce(subs, rank, pools, &vars);
|
||||||
|
|
||||||
let real_category =
|
// Figure out the problem - it might be pattern or value
|
||||||
constraints.categories[real_category.index()].clone();
|
// related.
|
||||||
let problem = TypeError::BadExpr(
|
let problem = match category_and_expected {
|
||||||
real_region,
|
Ok((category, expected)) => {
|
||||||
real_category,
|
let real_category =
|
||||||
actual_type,
|
constraints.categories[category.index()].clone();
|
||||||
expected_branches.replace_ref(expected_type),
|
TypeError::BadExpr(
|
||||||
);
|
real_region,
|
||||||
|
real_category,
|
||||||
|
actual_type,
|
||||||
|
expected.replace_ref(expected_type),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Err((category, expected)) => {
|
||||||
|
let real_category = constraints.pattern_categories
|
||||||
|
[category.index()]
|
||||||
|
.clone();
|
||||||
|
TypeError::BadPattern(
|
||||||
|
real_region,
|
||||||
|
real_category,
|
||||||
|
expected_type,
|
||||||
|
expected.replace_ref(actual_type),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
problems.push(problem);
|
problems.push(problem);
|
||||||
should_check_exhaustiveness = false;
|
should_check_exhaustiveness = false;
|
||||||
|
@ -1286,10 +1324,26 @@ fn solve(
|
||||||
let sketched_rows = constraints.sketched_rows[sketched_rows.index()].clone();
|
let sketched_rows = constraints.sketched_rows[sketched_rows.index()].clone();
|
||||||
|
|
||||||
if should_check_exhaustiveness {
|
if should_check_exhaustiveness {
|
||||||
let checked = roc_can::exhaustive::check(subs, sketched_rows, context);
|
use roc_can::exhaustive::{check, ExhaustiveSummary};
|
||||||
if let Err(errors) = checked {
|
|
||||||
problems.extend(errors.into_iter().map(TypeError::Exhaustive));
|
let ExhaustiveSummary {
|
||||||
|
errors,
|
||||||
|
exhaustive,
|
||||||
|
redundancies,
|
||||||
|
} = check(subs, sketched_rows, context);
|
||||||
|
|
||||||
|
// Store information about whether the "when" is exhaustive, and
|
||||||
|
// which (if any) of its branches are redundant. Codegen may use
|
||||||
|
// this for branch-fixing and redundant elimination.
|
||||||
|
if !exhaustive {
|
||||||
|
exhaustive_mark.set_non_exhaustive(subs);
|
||||||
}
|
}
|
||||||
|
for redundant_mark in redundancies {
|
||||||
|
redundant_mark.set_redundant(subs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the errors.
|
||||||
|
problems.extend(errors.into_iter().map(TypeError::Exhaustive));
|
||||||
}
|
}
|
||||||
|
|
||||||
state
|
state
|
||||||
|
|
|
@ -2503,7 +2503,7 @@ fn backpassing_result() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm"))]
|
||||||
#[should_panic(expected = "Shadowing { original_region: @57-58, shadow: @74-75 Ident")]
|
#[should_panic(expected = "Shadowing { original_region: @57-58, shadow: @74-75 Ident")]
|
||||||
fn function_malformed_pattern() {
|
fn function_malformed_pattern() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
|
|
|
@ -1676,6 +1676,8 @@ fn issue_2900_unreachable_pattern() {
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
RocStr::from("foo"),
|
RocStr::from("foo"),
|
||||||
RocStr
|
RocStr,
|
||||||
|
|x| x,
|
||||||
|
true // ignore type errors
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,15 +107,12 @@ pub fn helper(
|
||||||
let mut delayed_errors = Vec::new();
|
let mut delayed_errors = Vec::new();
|
||||||
|
|
||||||
for (home, (module_path, src)) in loaded.sources {
|
for (home, (module_path, src)) in loaded.sources {
|
||||||
use roc_reporting::report::{
|
use roc_reporting::report::{can_problem, type_problem, RocDocAllocator, DEFAULT_PALETTE};
|
||||||
can_problem, mono_problem, type_problem, RocDocAllocator, DEFAULT_PALETTE,
|
|
||||||
};
|
|
||||||
|
|
||||||
let can_problems = loaded.can_problems.remove(&home).unwrap_or_default();
|
let can_problems = loaded.can_problems.remove(&home).unwrap_or_default();
|
||||||
let type_problems = loaded.type_problems.remove(&home).unwrap_or_default();
|
let type_problems = loaded.type_problems.remove(&home).unwrap_or_default();
|
||||||
let mono_problems = loaded.mono_problems.remove(&home).unwrap_or_default();
|
|
||||||
|
|
||||||
let error_count = can_problems.len() + type_problems.len() + mono_problems.len();
|
let error_count = can_problems.len() + type_problems.len();
|
||||||
|
|
||||||
if error_count == 0 {
|
if error_count == 0 {
|
||||||
continue;
|
continue;
|
||||||
|
@ -156,15 +153,6 @@ pub fn helper(
|
||||||
lines.push(buf);
|
lines.push(buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for problem in mono_problems {
|
|
||||||
let report = mono_problem(&alloc, &line_info, module_path.clone(), problem);
|
|
||||||
let mut buf = String::new();
|
|
||||||
|
|
||||||
report.render_color_terminal(&mut buf, &alloc, &palette);
|
|
||||||
|
|
||||||
lines.push(buf);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !lines.is_empty() {
|
if !lines.is_empty() {
|
||||||
|
|
|
@ -83,15 +83,12 @@ fn create_llvm_module<'a>(
|
||||||
let mut delayed_errors = Vec::new();
|
let mut delayed_errors = Vec::new();
|
||||||
|
|
||||||
for (home, (module_path, src)) in loaded.sources {
|
for (home, (module_path, src)) in loaded.sources {
|
||||||
use roc_reporting::report::{
|
use roc_reporting::report::{can_problem, type_problem, RocDocAllocator, DEFAULT_PALETTE};
|
||||||
can_problem, mono_problem, type_problem, RocDocAllocator, DEFAULT_PALETTE,
|
|
||||||
};
|
|
||||||
|
|
||||||
let can_problems = loaded.can_problems.remove(&home).unwrap_or_default();
|
let can_problems = loaded.can_problems.remove(&home).unwrap_or_default();
|
||||||
let type_problems = loaded.type_problems.remove(&home).unwrap_or_default();
|
let type_problems = loaded.type_problems.remove(&home).unwrap_or_default();
|
||||||
let mono_problems = loaded.mono_problems.remove(&home).unwrap_or_default();
|
|
||||||
|
|
||||||
let error_count = can_problems.len() + type_problems.len() + mono_problems.len();
|
let error_count = can_problems.len() + type_problems.len();
|
||||||
|
|
||||||
if error_count == 0 {
|
if error_count == 0 {
|
||||||
continue;
|
continue;
|
||||||
|
@ -144,16 +141,6 @@ fn create_llvm_module<'a>(
|
||||||
lines.push(buf);
|
lines.push(buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for problem in mono_problems {
|
|
||||||
let report = mono_problem(&alloc, &line_info, module_path.clone(), problem);
|
|
||||||
let mut buf = String::new();
|
|
||||||
|
|
||||||
report.render_color_terminal(&mut buf, &alloc, &palette);
|
|
||||||
|
|
||||||
delayed_errors.push(buf.clone());
|
|
||||||
lines.push(buf);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !lines.is_empty() {
|
if !lines.is_empty() {
|
||||||
|
|
|
@ -5,9 +5,6 @@
|
||||||
// we actually want to compare against the literal float bits
|
// we actually want to compare against the literal float bits
|
||||||
#![allow(clippy::float_cmp)]
|
#![allow(clippy::float_cmp)]
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate pretty_assertions;
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate indoc;
|
extern crate indoc;
|
||||||
|
|
||||||
|
@ -123,14 +120,12 @@ fn compiles_to_ir(test_name: &str, src: &str) {
|
||||||
|
|
||||||
let can_problems = loaded.can_problems.remove(&home).unwrap_or_default();
|
let can_problems = loaded.can_problems.remove(&home).unwrap_or_default();
|
||||||
let type_problems = loaded.type_problems.remove(&home).unwrap_or_default();
|
let type_problems = loaded.type_problems.remove(&home).unwrap_or_default();
|
||||||
let mono_problems = loaded.mono_problems.remove(&home).unwrap_or_default();
|
|
||||||
|
|
||||||
if !can_problems.is_empty() {
|
if !can_problems.is_empty() {
|
||||||
println!("Ignoring {} canonicalization problems", can_problems.len());
|
println!("Ignoring {} canonicalization problems", can_problems.len());
|
||||||
}
|
}
|
||||||
|
|
||||||
assert!(type_problems.is_empty());
|
assert!(type_problems.is_empty());
|
||||||
assert_eq!(mono_problems, Vec::new());
|
|
||||||
|
|
||||||
debug_assert_eq!(exposed_to_host.values.len(), 1);
|
debug_assert_eq!(exposed_to_host.values.len(), 1);
|
||||||
|
|
||||||
|
|
|
@ -965,6 +965,77 @@ impl From<OptVariable> for Option<Variable> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Marks whether a when expression is exhaustive using a variable.
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct ExhaustiveMark(Variable);
|
||||||
|
|
||||||
|
impl ExhaustiveMark {
|
||||||
|
pub fn new(var_store: &mut VarStore) -> Self {
|
||||||
|
Self(var_store.fresh())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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(Variable::EMPTY_TAG_UNION)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn variable_for_introduction(&self) -> Variable {
|
||||||
|
debug_assert!(
|
||||||
|
self.0 != Variable::EMPTY_TAG_UNION,
|
||||||
|
"Attempting to introduce known mark"
|
||||||
|
);
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_non_exhaustive(&self, subs: &mut Subs) {
|
||||||
|
subs.set_content(self.0, Content::Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_non_exhaustive(&self, subs: &Subs) -> bool {
|
||||||
|
matches!(subs.get_content_without_compacting(self.0), Content::Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Marks whether a when branch is redundant using a variable.
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub struct RedundantMark(Variable);
|
||||||
|
|
||||||
|
impl RedundantMark {
|
||||||
|
pub fn new(var_store: &mut VarStore) -> Self {
|
||||||
|
Self(var_store.fresh())
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: only ever use this if you *know* a pattern match is surely exhaustive!
|
||||||
|
// Otherwise you will get unpleasant unification errors.
|
||||||
|
pub fn known_non_redundant() -> Self {
|
||||||
|
Self(Variable::EMPTY_TAG_UNION)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn variable_for_introduction(&self) -> Variable {
|
||||||
|
debug_assert!(
|
||||||
|
self.0 != Variable::EMPTY_TAG_UNION,
|
||||||
|
"Attempting to introduce known mark"
|
||||||
|
);
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_redundant(&self, subs: &mut Subs) {
|
||||||
|
subs.set_content(self.0, Content::Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_redundant(&self, subs: &Subs) -> bool {
|
||||||
|
matches!(subs.get_content_without_compacting(self.0), Content::Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_marks(var_store: &mut VarStore) -> (RedundantMark, ExhaustiveMark) {
|
||||||
|
(
|
||||||
|
RedundantMark::new(var_store),
|
||||||
|
ExhaustiveMark::new(var_store),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
pub struct Variable(u32);
|
pub struct Variable(u32);
|
||||||
|
|
||||||
|
|
|
@ -1731,6 +1731,10 @@ pub enum Reason {
|
||||||
name: Option<Symbol>,
|
name: Option<Symbol>,
|
||||||
arg_index: HumanIndex,
|
arg_index: HumanIndex,
|
||||||
},
|
},
|
||||||
|
TypedArg {
|
||||||
|
name: Option<Symbol>,
|
||||||
|
arg_index: HumanIndex,
|
||||||
|
},
|
||||||
FnCall {
|
FnCall {
|
||||||
name: Option<Symbol>,
|
name: Option<Symbol>,
|
||||||
arity: u8,
|
arity: u8,
|
||||||
|
@ -2508,7 +2512,10 @@ pub fn gather_tags_unsorted_iter(
|
||||||
// TODO investigate this likely can happen when there is a type error
|
// TODO investigate this likely can happen when there is a type error
|
||||||
RigidVar(_) => break,
|
RigidVar(_) => break,
|
||||||
|
|
||||||
other => unreachable!("something weird ended up in a tag union type: {:?}", other),
|
other => unreachable!(
|
||||||
|
"something weird ended up in a tag union type: {:?} at {:?}",
|
||||||
|
other, var
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,7 @@ use roc_module::symbol::Symbol;
|
||||||
use roc_types::subs::Content::{self, *};
|
use roc_types::subs::Content::{self, *};
|
||||||
use roc_types::subs::{
|
use roc_types::subs::{
|
||||||
AliasVariables, Descriptor, ErrorTypeContext, FlatType, GetSubsSlice, Mark, OptVariable,
|
AliasVariables, Descriptor, ErrorTypeContext, FlatType, GetSubsSlice, Mark, OptVariable,
|
||||||
RecordFields, Subs, SubsFmtContent, SubsIndex, SubsSlice, UnionTags, Variable,
|
RecordFields, Subs, SubsIndex, SubsSlice, UnionTags, Variable, VariableSubsSlice,
|
||||||
VariableSubsSlice,
|
|
||||||
};
|
};
|
||||||
use roc_types::types::{AliasKind, DoesNotImplementAbility, ErrorType, Mismatch, RecordField};
|
use roc_types::types::{AliasKind, DoesNotImplementAbility, ErrorType, Mismatch, RecordField};
|
||||||
|
|
||||||
|
@ -295,6 +294,8 @@ pub fn unify_pool(
|
||||||
/// NOTE: Only run this on individual tests! Run on multiple threads, this would clobber each others' output.
|
/// NOTE: Only run this on individual tests! Run on multiple threads, this would clobber each others' output.
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
fn debug_print_unified_types(subs: &mut Subs, ctx: &Context, opt_outcome: Option<&Outcome>) {
|
fn debug_print_unified_types(subs: &mut Subs, ctx: &Context, opt_outcome: Option<&Outcome>) {
|
||||||
|
use roc_types::subs::SubsFmtContent;
|
||||||
|
|
||||||
static mut UNIFICATION_DEPTH: usize = 0;
|
static mut UNIFICATION_DEPTH: usize = 0;
|
||||||
|
|
||||||
if std::env::var("ROC_PRINT_UNIFICATIONS").is_ok() {
|
if std::env::var("ROC_PRINT_UNIFICATIONS").is_ok() {
|
||||||
|
|
|
@ -7,7 +7,7 @@ use roc_fmt::annotation::{Newlines, Parens};
|
||||||
use roc_load::{LoadingProblem, MonomorphizedModule};
|
use roc_load::{LoadingProblem, MonomorphizedModule};
|
||||||
use roc_parse::ast::Expr;
|
use roc_parse::ast::Expr;
|
||||||
use roc_region::all::LineInfo;
|
use roc_region::all::LineInfo;
|
||||||
use roc_reporting::report::{can_problem, mono_problem, type_problem, RocDocAllocator};
|
use roc_reporting::report::{can_problem, type_problem, RocDocAllocator};
|
||||||
use roc_target::TargetInfo;
|
use roc_target::TargetInfo;
|
||||||
|
|
||||||
use crate::eval::ToAstProblem;
|
use crate::eval::ToAstProblem;
|
||||||
|
@ -78,7 +78,6 @@ pub fn compile_to_mono<'a>(
|
||||||
sources,
|
sources,
|
||||||
can_problems,
|
can_problems,
|
||||||
type_problems,
|
type_problems,
|
||||||
mono_problems,
|
|
||||||
..
|
..
|
||||||
} = &mut loaded;
|
} = &mut loaded;
|
||||||
|
|
||||||
|
@ -87,9 +86,8 @@ pub fn compile_to_mono<'a>(
|
||||||
for (home, (module_path, src)) in sources.iter() {
|
for (home, (module_path, src)) in sources.iter() {
|
||||||
let can_probs = can_problems.remove(home).unwrap_or_default();
|
let can_probs = can_problems.remove(home).unwrap_or_default();
|
||||||
let type_probs = type_problems.remove(home).unwrap_or_default();
|
let type_probs = type_problems.remove(home).unwrap_or_default();
|
||||||
let mono_probs = mono_problems.remove(home).unwrap_or_default();
|
|
||||||
|
|
||||||
let error_count = can_probs.len() + type_probs.len() + mono_probs.len();
|
let error_count = can_probs.len() + type_probs.len();
|
||||||
|
|
||||||
if error_count == 0 {
|
if error_count == 0 {
|
||||||
continue;
|
continue;
|
||||||
|
@ -119,15 +117,6 @@ pub fn compile_to_mono<'a>(
|
||||||
lines.push(buf);
|
lines.push(buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for problem in mono_probs {
|
|
||||||
let report = mono_problem(&alloc, &line_info, module_path.clone(), problem);
|
|
||||||
let mut buf = String::new();
|
|
||||||
|
|
||||||
report.render_color_terminal(&mut buf, &alloc, &palette);
|
|
||||||
|
|
||||||
lines.push(buf);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !lines.is_empty() {
|
if !lines.is_empty() {
|
||||||
|
|
|
@ -15,7 +15,6 @@ roc_problem = { path = "../compiler/problem" }
|
||||||
roc_types = { path = "../compiler/types" }
|
roc_types = { path = "../compiler/types" }
|
||||||
roc_can = { path = "../compiler/can" }
|
roc_can = { path = "../compiler/can" }
|
||||||
roc_solve = { path = "../compiler/solve" }
|
roc_solve = { path = "../compiler/solve" }
|
||||||
roc_mono = { path = "../compiler/mono" }
|
|
||||||
ven_pretty = { path = "../vendor/pretty" }
|
ven_pretty = { path = "../vendor/pretty" }
|
||||||
distance = "0.4.0"
|
distance = "0.4.0"
|
||||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
pub mod canonicalize;
|
pub mod canonicalize;
|
||||||
pub mod mono;
|
|
||||||
pub mod parse;
|
pub mod parse;
|
||||||
pub mod r#type;
|
pub mod r#type;
|
||||||
|
|
|
@ -1,235 +0,0 @@
|
||||||
use crate::report::{Annotation, Report, RocDocAllocator, RocDocBuilder, Severity};
|
|
||||||
use roc_module::ident::TagName;
|
|
||||||
use roc_region::all::LineInfo;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use ven_pretty::DocAllocator;
|
|
||||||
|
|
||||||
pub fn mono_problem<'b>(
|
|
||||||
alloc: &'b RocDocAllocator<'b>,
|
|
||||||
lines: &LineInfo,
|
|
||||||
filename: PathBuf,
|
|
||||||
problem: roc_mono::ir::MonoProblem,
|
|
||||||
) -> Report<'b> {
|
|
||||||
use roc_exhaustive::Context::*;
|
|
||||||
use roc_exhaustive::Error::*;
|
|
||||||
use roc_mono::ir::MonoProblem::*;
|
|
||||||
|
|
||||||
match problem {
|
|
||||||
PatternProblem(Incomplete(region, context, missing)) => match context {
|
|
||||||
BadArg => {
|
|
||||||
let doc = alloc.stack([
|
|
||||||
alloc.reflow("This pattern does not cover all the possibilities:"),
|
|
||||||
alloc.region(lines.convert_region(region)),
|
|
||||||
alloc.reflow("Other possibilities include:"),
|
|
||||||
unhandled_patterns_to_doc_block(alloc, missing),
|
|
||||||
alloc.concat([
|
|
||||||
alloc.reflow(
|
|
||||||
"I would have to crash if I saw one of those! \
|
|
||||||
So rather than pattern matching in function arguments, put a ",
|
|
||||||
),
|
|
||||||
alloc.keyword("when"),
|
|
||||||
alloc.reflow(" in the function body to account for all possibilities."),
|
|
||||||
]),
|
|
||||||
]);
|
|
||||||
|
|
||||||
Report {
|
|
||||||
filename,
|
|
||||||
title: "UNSAFE PATTERN".to_string(),
|
|
||||||
doc,
|
|
||||||
severity: Severity::RuntimeError,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BadDestruct => {
|
|
||||||
let doc = alloc.stack([
|
|
||||||
alloc.reflow("This pattern does not cover all the possibilities:"),
|
|
||||||
alloc.region(lines.convert_region(region)),
|
|
||||||
alloc.reflow("Other possibilities include:"),
|
|
||||||
unhandled_patterns_to_doc_block(alloc, missing),
|
|
||||||
alloc.concat([
|
|
||||||
alloc.reflow(
|
|
||||||
"I would have to crash if I saw one of those! \
|
|
||||||
You can use a binding to deconstruct a value if there is only ONE possibility. \
|
|
||||||
Use a "
|
|
||||||
),
|
|
||||||
alloc.keyword("when"),
|
|
||||||
alloc.reflow(" to account for all possibilities."),
|
|
||||||
]),
|
|
||||||
]);
|
|
||||||
|
|
||||||
Report {
|
|
||||||
filename,
|
|
||||||
title: "UNSAFE PATTERN".to_string(),
|
|
||||||
doc,
|
|
||||||
severity: Severity::RuntimeError,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BadCase => {
|
|
||||||
let doc = alloc.stack([
|
|
||||||
alloc.concat([
|
|
||||||
alloc.reflow("This "),
|
|
||||||
alloc.keyword("when"),
|
|
||||||
alloc.reflow(" does not cover all the possibilities:"),
|
|
||||||
]),
|
|
||||||
alloc.region(lines.convert_region(region)),
|
|
||||||
alloc.reflow("Other possibilities include:"),
|
|
||||||
unhandled_patterns_to_doc_block(alloc, missing),
|
|
||||||
alloc.reflow(
|
|
||||||
"I would have to crash if I saw one of those! \
|
|
||||||
Add branches for them!",
|
|
||||||
),
|
|
||||||
// alloc.hint().append(alloc.reflow("or use a hole.")),
|
|
||||||
]);
|
|
||||||
|
|
||||||
Report {
|
|
||||||
filename,
|
|
||||||
title: "UNSAFE PATTERN".to_string(),
|
|
||||||
doc,
|
|
||||||
severity: Severity::RuntimeError,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
PatternProblem(Redundant {
|
|
||||||
overall_region,
|
|
||||||
branch_region,
|
|
||||||
index,
|
|
||||||
}) => {
|
|
||||||
let doc = alloc.stack([
|
|
||||||
alloc.concat([
|
|
||||||
alloc.reflow("The "),
|
|
||||||
alloc.string(index.ordinal()),
|
|
||||||
alloc.reflow(" pattern is redundant:"),
|
|
||||||
]),
|
|
||||||
alloc.region_with_subregion(
|
|
||||||
lines.convert_region(overall_region),
|
|
||||||
lines.convert_region(branch_region),
|
|
||||||
),
|
|
||||||
alloc.reflow(
|
|
||||||
"Any value of this shape will be handled by \
|
|
||||||
a previous pattern, so this one should be removed.",
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
|
|
||||||
Report {
|
|
||||||
filename,
|
|
||||||
title: "REDUNDANT PATTERN".to_string(),
|
|
||||||
doc,
|
|
||||||
severity: Severity::Warning,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unhandled_patterns_to_doc_block<'b>(
|
|
||||||
alloc: &'b RocDocAllocator<'b>,
|
|
||||||
patterns: Vec<roc_exhaustive::Pattern>,
|
|
||||||
) -> RocDocBuilder<'b> {
|
|
||||||
alloc
|
|
||||||
.vcat(patterns.into_iter().map(|v| pattern_to_doc(alloc, v)))
|
|
||||||
.indent(4)
|
|
||||||
.annotate(Annotation::TypeBlock)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pattern_to_doc<'b>(
|
|
||||||
alloc: &'b RocDocAllocator<'b>,
|
|
||||||
pattern: roc_exhaustive::Pattern,
|
|
||||||
) -> RocDocBuilder<'b> {
|
|
||||||
pattern_to_doc_help(alloc, pattern, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
const AFTER_TAG_INDENT: &str = " ";
|
|
||||||
|
|
||||||
fn pattern_to_doc_help<'b>(
|
|
||||||
alloc: &'b RocDocAllocator<'b>,
|
|
||||||
pattern: roc_exhaustive::Pattern,
|
|
||||||
in_type_param: bool,
|
|
||||||
) -> RocDocBuilder<'b> {
|
|
||||||
use roc_exhaustive::Literal::*;
|
|
||||||
use roc_exhaustive::Pattern::*;
|
|
||||||
use roc_exhaustive::{CtorName, RenderAs};
|
|
||||||
|
|
||||||
match pattern {
|
|
||||||
Anything => alloc.text("_"),
|
|
||||||
Literal(l) => match l {
|
|
||||||
Int(i) => alloc.text(i.to_string()),
|
|
||||||
U128(i) => alloc.text(i.to_string()),
|
|
||||||
Bit(true) => alloc.text("True"),
|
|
||||||
Bit(false) => alloc.text("False"),
|
|
||||||
Byte(b) => alloc.text(b.to_string()),
|
|
||||||
Float(f) => alloc.text(f.to_string()),
|
|
||||||
Decimal(d) => alloc.text(d.to_string()),
|
|
||||||
Str(s) => alloc.string(s.into()),
|
|
||||||
},
|
|
||||||
Ctor(union, tag_id, args) => {
|
|
||||||
match union.render_as {
|
|
||||||
RenderAs::Guard => {
|
|
||||||
// #Guard <fake-condition-tag> <unexhausted-pattern>
|
|
||||||
debug_assert!(union.alternatives[tag_id.0 as usize]
|
|
||||||
.name
|
|
||||||
.is_tag(&TagName::Tag("#Guard".into())),);
|
|
||||||
debug_assert!(args.len() == 2);
|
|
||||||
let tag = pattern_to_doc_help(alloc, args[1].clone(), in_type_param);
|
|
||||||
alloc.concat([
|
|
||||||
tag,
|
|
||||||
alloc.text(AFTER_TAG_INDENT),
|
|
||||||
alloc.text("(note the lack of an "),
|
|
||||||
alloc.keyword("if"),
|
|
||||||
alloc.text(" clause)"),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
RenderAs::Record(field_names) => {
|
|
||||||
let mut arg_docs = Vec::with_capacity(args.len());
|
|
||||||
|
|
||||||
for (label, v) in field_names.into_iter().zip(args.into_iter()) {
|
|
||||||
match &v {
|
|
||||||
Anything => {
|
|
||||||
arg_docs.push(alloc.text(label.to_string()));
|
|
||||||
}
|
|
||||||
Literal(_) | Ctor(_, _, _) => {
|
|
||||||
arg_docs.push(
|
|
||||||
alloc
|
|
||||||
.text(label.to_string())
|
|
||||||
.append(alloc.reflow(": "))
|
|
||||||
.append(pattern_to_doc_help(alloc, v, false)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
alloc
|
|
||||||
.text("{ ")
|
|
||||||
.append(alloc.intersperse(arg_docs, alloc.reflow(", ")))
|
|
||||||
.append(" }")
|
|
||||||
}
|
|
||||||
RenderAs::Tag | RenderAs::Opaque => {
|
|
||||||
let has_args = !args.is_empty();
|
|
||||||
let arg_docs = args
|
|
||||||
.into_iter()
|
|
||||||
.map(|v| pattern_to_doc_help(alloc, v, true));
|
|
||||||
|
|
||||||
let ctor = &union.alternatives[tag_id.0 as usize];
|
|
||||||
let tag_name = match (union.render_as, &ctor.name) {
|
|
||||||
(RenderAs::Tag, CtorName::Tag(tag)) => alloc.tag_name(tag.clone()),
|
|
||||||
(RenderAs::Opaque, CtorName::Opaque(opaque)) => {
|
|
||||||
alloc.wrapped_opaque_name(*opaque)
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// We assume the alternatives are sorted. If not, this assert will trigger
|
|
||||||
debug_assert!(tag_id == ctor.tag_id);
|
|
||||||
|
|
||||||
let docs = std::iter::once(tag_name).chain(arg_docs);
|
|
||||||
|
|
||||||
if in_type_param && has_args {
|
|
||||||
alloc
|
|
||||||
.text("(")
|
|
||||||
.append(alloc.intersperse(docs, alloc.space()))
|
|
||||||
.append(")")
|
|
||||||
} else {
|
|
||||||
alloc.intersperse(docs, alloc.space())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1204,6 +1204,43 @@ fn to_expr_report<'b>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reason::TypedArg { name, arg_index } => {
|
||||||
|
let name = match name {
|
||||||
|
Some(n) => alloc.symbol_unqualified(n),
|
||||||
|
None => alloc.text(" this definition "),
|
||||||
|
};
|
||||||
|
let doc = alloc.stack([
|
||||||
|
alloc
|
||||||
|
.text("The ")
|
||||||
|
.append(alloc.text(arg_index.ordinal()))
|
||||||
|
.append(alloc.text(" argument to "))
|
||||||
|
.append(name.clone())
|
||||||
|
.append(alloc.text(" is weird:")),
|
||||||
|
alloc.region(lines.convert_region(region)),
|
||||||
|
pattern_type_comparison(
|
||||||
|
alloc,
|
||||||
|
expected_type,
|
||||||
|
found,
|
||||||
|
add_category(alloc, alloc.text("The argument matches"), &category),
|
||||||
|
alloc.concat([
|
||||||
|
alloc.text("But the annotation on "),
|
||||||
|
name,
|
||||||
|
alloc.text(" says the "),
|
||||||
|
alloc.text(arg_index.ordinal()),
|
||||||
|
alloc.text(" argument should be:"),
|
||||||
|
]),
|
||||||
|
vec![],
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
|
Report {
|
||||||
|
filename,
|
||||||
|
title: "TYPE MISMATCH".to_string(),
|
||||||
|
doc,
|
||||||
|
severity: Severity::RuntimeError,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reason::LowLevelOpArg { op, arg_index } => {
|
Reason::LowLevelOpArg { op, arg_index } => {
|
||||||
panic!(
|
panic!(
|
||||||
"Compiler bug: argument #{} to low-level operation {:?} was the wrong type!",
|
"Compiler bug: argument #{} to low-level operation {:?} was the wrong type!",
|
||||||
|
@ -2312,7 +2349,6 @@ fn to_diff<'b>(
|
||||||
// Skip the hint for numbers; it's not as useful as saying "this type is not a number"
|
// Skip the hint for numbers; it's not as useful as saying "this type is not a number"
|
||||||
if !OPAQUE_NUM_SYMBOLS.contains(&sym) =>
|
if !OPAQUE_NUM_SYMBOLS.contains(&sym) =>
|
||||||
{
|
{
|
||||||
dbg!(&type1, &type2);
|
|
||||||
let (left, left_able) = to_doc(alloc, Parens::InFn, type1);
|
let (left, left_able) = to_doc(alloc, Parens::InFn, type1);
|
||||||
let (right, right_able) = to_doc(alloc, Parens::InFn, type2);
|
let (right, right_able) = to_doc(alloc, Parens::InFn, type2);
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ use std::path::{Path, PathBuf};
|
||||||
use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder, Render, RenderAnnotated};
|
use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder, Render, RenderAnnotated};
|
||||||
|
|
||||||
pub use crate::error::canonicalize::can_problem;
|
pub use crate::error::canonicalize::can_problem;
|
||||||
pub use crate::error::mono::mono_problem;
|
|
||||||
pub use crate::error::parse::parse_problem;
|
pub use crate::error::parse::parse_problem;
|
||||||
pub use crate::error::r#type::type_problem;
|
pub use crate::error::r#type::type_problem;
|
||||||
|
|
||||||
|
|
|
@ -12,16 +12,12 @@ mod test_reporting {
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
use roc_can::abilities::AbilitiesStore;
|
use roc_can::abilities::AbilitiesStore;
|
||||||
use roc_can::def::Declaration;
|
|
||||||
use roc_can::pattern::Pattern;
|
|
||||||
use roc_load::{self, LoadedModule, LoadingProblem};
|
use roc_load::{self, LoadedModule, LoadingProblem};
|
||||||
use roc_module::symbol::{Interns, ModuleId};
|
use roc_module::symbol::{Interns, ModuleId};
|
||||||
use roc_mono::ir::{Procs, Stmt, UpdateModeIds};
|
|
||||||
use roc_mono::layout::LayoutCache;
|
|
||||||
use roc_region::all::LineInfo;
|
use roc_region::all::LineInfo;
|
||||||
use roc_reporting::report::{
|
use roc_reporting::report::{
|
||||||
can_problem, mono_problem, parse_problem, type_problem, RenderTarget, Report, Severity,
|
can_problem, parse_problem, type_problem, RenderTarget, Report, Severity, ANSI_STYLE_CODES,
|
||||||
ANSI_STYLE_CODES, DEFAULT_PALETTE,
|
DEFAULT_PALETTE,
|
||||||
};
|
};
|
||||||
use roc_reporting::report::{RocDocAllocator, RocDocBuilder};
|
use roc_reporting::report::{RocDocAllocator, RocDocBuilder};
|
||||||
use roc_solve::solve;
|
use roc_solve::solve;
|
||||||
|
@ -114,7 +110,6 @@ mod test_reporting {
|
||||||
String,
|
String,
|
||||||
Vec<solve::TypeError>,
|
Vec<solve::TypeError>,
|
||||||
Vec<roc_problem::can::Problem>,
|
Vec<roc_problem::can::Problem>,
|
||||||
Vec<roc_mono::ir::MonoProblem>,
|
|
||||||
ModuleId,
|
ModuleId,
|
||||||
Interns,
|
Interns,
|
||||||
),
|
),
|
||||||
|
@ -128,8 +123,6 @@ mod test_reporting {
|
||||||
interns,
|
interns,
|
||||||
mut solved,
|
mut solved,
|
||||||
exposed_to_host,
|
exposed_to_host,
|
||||||
mut declarations_by_id,
|
|
||||||
abilities_store,
|
|
||||||
..
|
..
|
||||||
} = result?;
|
} = result?;
|
||||||
|
|
||||||
|
@ -142,65 +135,7 @@ mod test_reporting {
|
||||||
name_all_type_vars(*var, subs);
|
name_all_type_vars(*var, subs);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut mono_problems = Vec::new();
|
Ok((module_src, type_problems, can_problems, home, interns))
|
||||||
|
|
||||||
// MONO
|
|
||||||
|
|
||||||
if type_problems.is_empty() && can_problems.is_empty() {
|
|
||||||
let arena = Bump::new();
|
|
||||||
|
|
||||||
assert!(exposed_to_host.len() == 1);
|
|
||||||
let (sym, _var) = exposed_to_host.into_iter().next().unwrap();
|
|
||||||
|
|
||||||
let home_decls = declarations_by_id.remove(&home).unwrap();
|
|
||||||
let (loc_expr, var) = home_decls
|
|
||||||
.into_iter()
|
|
||||||
.find_map(|decl| match decl {
|
|
||||||
Declaration::Declare(def) => match def.loc_pattern.value {
|
|
||||||
Pattern::Identifier(s) if s == sym => Some((def.loc_expr, def.expr_var)),
|
|
||||||
_ => None,
|
|
||||||
},
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.expect("No expression to monomorphize found!");
|
|
||||||
|
|
||||||
// Compile and add all the Procs before adding main
|
|
||||||
let mut procs = Procs::new_in(&arena);
|
|
||||||
let mut ident_ids = interns.all_ident_ids.get(&home).unwrap().clone();
|
|
||||||
let mut update_mode_ids = UpdateModeIds::new();
|
|
||||||
|
|
||||||
// Populate Procs and Subs, and get the low-level Expr from the canonical Expr
|
|
||||||
let target_info = roc_target::TargetInfo::default_x86_64();
|
|
||||||
let mut layout_cache = LayoutCache::new(target_info);
|
|
||||||
let mut mono_env = roc_mono::ir::Env {
|
|
||||||
arena: &arena,
|
|
||||||
subs,
|
|
||||||
problems: &mut mono_problems,
|
|
||||||
home,
|
|
||||||
ident_ids: &mut ident_ids,
|
|
||||||
update_mode_ids: &mut update_mode_ids,
|
|
||||||
target_info,
|
|
||||||
// call_specialization_counter=0 is reserved
|
|
||||||
call_specialization_counter: 1,
|
|
||||||
abilities_store: &abilities_store,
|
|
||||||
};
|
|
||||||
let _mono_expr = Stmt::new(
|
|
||||||
&mut mono_env,
|
|
||||||
loc_expr.value,
|
|
||||||
var,
|
|
||||||
&mut procs,
|
|
||||||
&mut layout_cache,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
module_src,
|
|
||||||
type_problems,
|
|
||||||
can_problems,
|
|
||||||
mono_problems,
|
|
||||||
home,
|
|
||||||
interns,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_reports_new<F>(subdir: &str, arena: &Bump, src: &str, finalize_render: F) -> String
|
fn list_reports_new<F>(subdir: &str, arena: &Bump, src: &str, finalize_render: F) -> String
|
||||||
|
@ -215,7 +150,7 @@ mod test_reporting {
|
||||||
|
|
||||||
match infer_expr_help_new(subdir, arena, src) {
|
match infer_expr_help_new(subdir, arena, src) {
|
||||||
Err(LoadingProblem::FormattedReport(fail)) => fail,
|
Err(LoadingProblem::FormattedReport(fail)) => fail,
|
||||||
Ok((module_src, type_problems, can_problems, mono_problems, home, interns)) => {
|
Ok((module_src, type_problems, can_problems, home, interns)) => {
|
||||||
let lines = LineInfo::new(&module_src);
|
let lines = LineInfo::new(&module_src);
|
||||||
let src_lines: Vec<&str> = module_src.split('\n').collect();
|
let src_lines: Vec<&str> = module_src.split('\n').collect();
|
||||||
let mut reports = Vec::new();
|
let mut reports = Vec::new();
|
||||||
|
@ -235,11 +170,6 @@ mod test_reporting {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for problem in mono_problems {
|
|
||||||
let report = mono_problem(&alloc, &lines, filename.clone(), problem.clone());
|
|
||||||
reports.push(report);
|
|
||||||
}
|
|
||||||
|
|
||||||
let has_reports = !reports.is_empty();
|
let has_reports = !reports.is_empty();
|
||||||
|
|
||||||
let doc = alloc
|
let doc = alloc
|
||||||
|
@ -267,14 +197,13 @@ mod test_reporting {
|
||||||
(
|
(
|
||||||
Vec<solve::TypeError>,
|
Vec<solve::TypeError>,
|
||||||
Vec<roc_problem::can::Problem>,
|
Vec<roc_problem::can::Problem>,
|
||||||
Vec<roc_mono::ir::MonoProblem>,
|
|
||||||
ModuleId,
|
ModuleId,
|
||||||
Interns,
|
Interns,
|
||||||
),
|
),
|
||||||
ParseErrOut<'a>,
|
ParseErrOut<'a>,
|
||||||
> {
|
> {
|
||||||
let CanExprOut {
|
let CanExprOut {
|
||||||
loc_expr,
|
loc_expr: _,
|
||||||
output,
|
output,
|
||||||
var_store,
|
var_store,
|
||||||
var,
|
var,
|
||||||
|
@ -315,43 +244,7 @@ mod test_reporting {
|
||||||
|
|
||||||
name_all_type_vars(var, &mut subs);
|
name_all_type_vars(var, &mut subs);
|
||||||
|
|
||||||
let mut mono_problems = Vec::new();
|
Ok((unify_problems, can_problems, home, interns))
|
||||||
|
|
||||||
// MONO
|
|
||||||
|
|
||||||
if unify_problems.is_empty() && can_problems.is_empty() {
|
|
||||||
let arena = Bump::new();
|
|
||||||
|
|
||||||
// Compile and add all the Procs before adding main
|
|
||||||
let mut procs = Procs::new_in(&arena);
|
|
||||||
let mut ident_ids = interns.all_ident_ids.get(&home).unwrap().clone();
|
|
||||||
let mut update_mode_ids = UpdateModeIds::new();
|
|
||||||
|
|
||||||
// Populate Procs and Subs, and get the low-level Expr from the canonical Expr
|
|
||||||
let target_info = roc_target::TargetInfo::default_x86_64();
|
|
||||||
let mut layout_cache = LayoutCache::new(target_info);
|
|
||||||
let mut mono_env = roc_mono::ir::Env {
|
|
||||||
arena: &arena,
|
|
||||||
subs: &mut subs,
|
|
||||||
problems: &mut mono_problems,
|
|
||||||
home,
|
|
||||||
ident_ids: &mut ident_ids,
|
|
||||||
update_mode_ids: &mut update_mode_ids,
|
|
||||||
target_info,
|
|
||||||
// call_specialization_counter=0 is reserved
|
|
||||||
call_specialization_counter: 1,
|
|
||||||
abilities_store: &abilities_store,
|
|
||||||
};
|
|
||||||
let _mono_expr = Stmt::new(
|
|
||||||
&mut mono_env,
|
|
||||||
loc_expr.value,
|
|
||||||
var,
|
|
||||||
&mut procs,
|
|
||||||
&mut layout_cache,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((unify_problems, can_problems, mono_problems, home, interns))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_reports<F>(arena: &Bump, src: &str, buf: &mut String, callback: F)
|
fn list_reports<F>(arena: &Bump, src: &str, buf: &mut String, callback: F)
|
||||||
|
@ -380,7 +273,7 @@ mod test_reporting {
|
||||||
|
|
||||||
callback(doc.pretty(&alloc).append(alloc.line()), buf)
|
callback(doc.pretty(&alloc).append(alloc.line()), buf)
|
||||||
}
|
}
|
||||||
Ok((type_problems, can_problems, mono_problems, home, interns)) => {
|
Ok((type_problems, can_problems, home, interns)) => {
|
||||||
let mut reports = Vec::new();
|
let mut reports = Vec::new();
|
||||||
|
|
||||||
let alloc = RocDocAllocator::new(&src_lines, home, &interns);
|
let alloc = RocDocAllocator::new(&src_lines, home, &interns);
|
||||||
|
@ -398,11 +291,6 @@ mod test_reporting {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for problem in mono_problems {
|
|
||||||
let report = mono_problem(&alloc, &lines, filename.clone(), problem.clone());
|
|
||||||
reports.push(report);
|
|
||||||
}
|
|
||||||
|
|
||||||
let has_reports = !reports.is_empty();
|
let has_reports = !reports.is_empty();
|
||||||
|
|
||||||
let doc = alloc
|
let doc = alloc
|
||||||
|
@ -922,7 +810,7 @@ mod test_reporting {
|
||||||
);
|
);
|
||||||
|
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let (_type_problems, _can_problems, _mono_problems, home, interns) =
|
let (_type_problems, _can_problems, home, interns) =
|
||||||
infer_expr_help(&arena, src).expect("parse error");
|
infer_expr_help(&arena, src).expect("parse error");
|
||||||
|
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
|
@ -953,7 +841,7 @@ mod test_reporting {
|
||||||
);
|
);
|
||||||
|
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let (_type_problems, _can_problems, _mono_problems, home, mut interns) =
|
let (_type_problems, _can_problems, home, mut interns) =
|
||||||
infer_expr_help(&arena, src).expect("parse error");
|
infer_expr_help(&arena, src).expect("parse error");
|
||||||
|
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
|
@ -8487,16 +8375,16 @@ I need all branches in an `if` to have the same type!
|
||||||
r#"
|
r#"
|
||||||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||||||
|
|
||||||
This pattern is being used in an unexpected way:
|
The 1st argument to `f` is weird:
|
||||||
|
|
||||||
4│ f = \Age n -> n
|
4│ f = \Age n -> n
|
||||||
^^^^^
|
^^^^^
|
||||||
|
|
||||||
It is a `Age` tag of type:
|
The argument is a pattern that matches a `Age` tag of type:
|
||||||
|
|
||||||
[ Age a ]
|
[ Age a ]
|
||||||
|
|
||||||
But it needs to match:
|
But the annotation on `f` says the 1st argument should be:
|
||||||
|
|
||||||
Age
|
Age
|
||||||
|
|
||||||
|
@ -9861,6 +9749,7 @@ I need all branches in an `if` to have the same type!
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
fn imports_missing_comma() {
|
fn imports_missing_comma() {
|
||||||
new_report_problem_as(
|
new_report_problem_as(
|
||||||
"imports_missing_comma",
|
"imports_missing_comma",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue