Merge pull request #2971 from rtfeldman/no-mono-exhaustiveness

No more exhaustiveness checking in mono, nor mono errors
This commit is contained in:
Folkert de Vries 2022-04-29 09:18:50 +02:00 committed by GitHub
commit 4074088a5e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 770 additions and 781 deletions

View file

@ -1,5 +1,5 @@
use crate::def::Def;
use crate::expr::{self, ClosureData, Expr::*, IntValue};
use crate::expr::{self, AnnotatedMark, ClosureData, Expr::*, IntValue};
use crate::expr::{Expr, Field, Recursive};
use crate::num::{FloatBound, IntBound, IntWidth, NumericBound};
use crate::pattern::Pattern;
@ -9,7 +9,7 @@ use roc_module::ident::{Lowercase, TagName};
use roc_module::low_level::LowLevel;
use roc_module::symbol::Symbol;
use roc_region::all::{Loc, Region};
use roc_types::subs::{VarStore, Variable};
use roc_types::subs::{ExhaustiveMark, RedundantMark, VarStore, Variable};
macro_rules! macro_magic {
(@single $($x:tt)*) => (());
@ -2622,8 +2622,16 @@ fn list_intersperse(symbol: Symbol, var_store: &mut VarStore) -> Def {
recursive: Recursive::NotRecursive,
captured_symbols: vec![(sep_sym, sep_var)],
arguments: vec![
(clos_acc_var, no_region(Pattern::Identifier(clos_acc_sym))),
(sep_var, no_region(Pattern::Identifier(clos_elem_sym))),
(
clos_acc_var,
AnnotatedMark::new(var_store),
no_region(Pattern::Identifier(clos_acc_sym)),
),
(
sep_var,
AnnotatedMark::new(var_store),
no_region(Pattern::Identifier(clos_elem_sym)),
),
],
loc_body: {
let append_sep = RunLowLevel {
@ -2708,9 +2716,14 @@ fn list_split(symbol: Symbol, var_store: &mut VarStore) -> Def {
arguments: vec![
(
clos_start_var,
AnnotatedMark::new(var_store),
no_region(Pattern::Identifier(clos_start_sym)),
),
(clos_len_var, no_region(Pattern::Identifier(clos_len_sym))),
(
clos_len_var,
AnnotatedMark::new(var_store),
no_region(Pattern::Identifier(clos_len_sym)),
),
],
loc_body: {
Box::new(no_region(RunLowLevel {
@ -2894,7 +2907,11 @@ fn list_drop_if(symbol: Symbol, var_store: &mut VarStore) -> Def {
name: Symbol::LIST_DROP_IF_PREDICATE,
recursive: Recursive::NotRecursive,
captured_symbols: vec![(sym_predicate, t_predicate)],
arguments: vec![(t_elem, no_region(Pattern::Identifier(Symbol::ARG_3)))],
arguments: vec![(
t_elem,
AnnotatedMark::new(var_store),
no_region(Pattern::Identifier(Symbol::ARG_3)),
)],
loc_body: {
let should_drop = Call(
Box::new((
@ -3078,8 +3095,16 @@ fn list_join_map(symbol: Symbol, var_store: &mut VarStore) -> Def {
recursive: Recursive::NotRecursive,
captured_symbols: vec![(Symbol::ARG_2, before2list_after)],
arguments: vec![
(list_after, no_region(Pattern::Identifier(Symbol::ARG_3))),
(before, no_region(Pattern::Identifier(Symbol::ARG_4))),
(
list_after,
AnnotatedMark::new(var_store),
no_region(Pattern::Identifier(Symbol::ARG_3)),
),
(
before,
AnnotatedMark::new(var_store),
no_region(Pattern::Identifier(Symbol::ARG_4)),
),
],
loc_body: {
let mapper = Box::new((
@ -3609,8 +3634,16 @@ fn list_sort_desc(symbol: Symbol, var_store: &mut VarStore) -> Def {
recursive: Recursive::NotRecursive,
captured_symbols: vec![],
arguments: vec![
(num_var, no_region(Pattern::Identifier(Symbol::ARG_2))),
(num_var, no_region(Pattern::Identifier(Symbol::ARG_3))),
(
num_var,
AnnotatedMark::new(var_store),
no_region(Pattern::Identifier(Symbol::ARG_2)),
),
(
num_var,
AnnotatedMark::new(var_store),
no_region(Pattern::Identifier(Symbol::ARG_3)),
),
],
loc_body: {
Box::new(no_region(RunLowLevel {
@ -4084,9 +4117,21 @@ fn set_walk(symbol: Symbol, var_store: &mut VarStore) -> Def {
recursive: Recursive::NotRecursive,
captured_symbols: vec![(Symbol::ARG_3, func_var)],
arguments: vec![
(accum_var, no_region(Pattern::Identifier(Symbol::ARG_5))),
(key_var, no_region(Pattern::Identifier(Symbol::ARG_6))),
(Variable::EMPTY_RECORD, no_region(Pattern::Underscore)),
(
accum_var,
AnnotatedMark::new(var_store),
no_region(Pattern::Identifier(Symbol::ARG_5)),
),
(
key_var,
AnnotatedMark::new(var_store),
no_region(Pattern::Identifier(Symbol::ARG_6)),
),
(
Variable::EMPTY_RECORD,
AnnotatedMark::new(var_store),
no_region(Pattern::Underscore),
),
],
loc_body: Box::new(no_region(call_func)),
});
@ -4686,6 +4731,7 @@ fn result_map(symbol: Symbol, var_store: &mut VarStore) -> Def {
patterns: vec![no_region(pattern)],
value: no_region(ok),
guard: None,
redundant: RedundantMark::new(var_store),
};
branches.push(branch);
@ -4716,6 +4762,7 @@ fn result_map(symbol: Symbol, var_store: &mut VarStore) -> Def {
patterns: vec![no_region(pattern)],
value: no_region(err),
guard: None,
redundant: RedundantMark::new(var_store),
};
branches.push(branch);
@ -4728,6 +4775,7 @@ fn result_map(symbol: Symbol, var_store: &mut VarStore) -> Def {
loc_cond: Box::new(no_region(Var(Symbol::ARG_1))),
branches,
branches_cond_var: var_store.fresh(),
exhaustive: ExhaustiveMark::new(var_store),
};
defn(
@ -4784,6 +4832,7 @@ fn result_map_err(symbol: Symbol, var_store: &mut VarStore) -> Def {
patterns: vec![no_region(pattern)],
value: no_region(ok),
guard: None,
redundant: RedundantMark::new(var_store),
};
branches.push(branch);
@ -4814,6 +4863,7 @@ fn result_map_err(symbol: Symbol, var_store: &mut VarStore) -> Def {
patterns: vec![no_region(pattern)],
value: no_region(err),
guard: None,
redundant: RedundantMark::new(var_store),
};
branches.push(branch);
@ -4826,6 +4876,7 @@ fn result_map_err(symbol: Symbol, var_store: &mut VarStore) -> Def {
loc_cond: Box::new(no_region(Var(Symbol::ARG_1))),
branches,
branches_cond_var: var_store.fresh(),
exhaustive: ExhaustiveMark::new(var_store),
};
defn(
@ -4858,6 +4909,7 @@ fn result_with_default(symbol: Symbol, var_store: &mut VarStore) -> Def {
patterns: vec![no_region(pattern)],
value: no_region(Var(Symbol::ARG_3)),
guard: None,
redundant: RedundantMark::new(var_store),
};
branches.push(branch);
@ -4878,6 +4930,7 @@ fn result_with_default(symbol: Symbol, var_store: &mut VarStore) -> Def {
patterns: vec![no_region(pattern)],
value: no_region(Var(Symbol::ARG_2)),
guard: None,
redundant: RedundantMark::new(var_store),
};
branches.push(branch);
@ -4890,6 +4943,7 @@ fn result_with_default(symbol: Symbol, var_store: &mut VarStore) -> Def {
loc_cond: Box::new(no_region(Var(Symbol::ARG_1))),
branches,
branches_cond_var: var_store.fresh(),
exhaustive: ExhaustiveMark::new(var_store),
};
defn(
@ -4929,6 +4983,7 @@ fn result_is_err(symbol: Symbol, var_store: &mut VarStore) -> Def {
patterns: vec![no_region(pattern)],
value: no_region(false_expr),
guard: None,
redundant: RedundantMark::new(var_store),
};
branches.push(branch);
@ -4956,6 +5011,7 @@ fn result_is_err(symbol: Symbol, var_store: &mut VarStore) -> Def {
patterns: vec![no_region(pattern)],
value: no_region(true_expr),
guard: None,
redundant: RedundantMark::new(var_store),
};
branches.push(branch);
@ -4968,6 +5024,7 @@ fn result_is_err(symbol: Symbol, var_store: &mut VarStore) -> Def {
loc_cond: Box::new(no_region(Var(Symbol::ARG_1))),
branches,
branches_cond_var: var_store.fresh(),
exhaustive: ExhaustiveMark::new(var_store),
};
defn(
@ -5007,6 +5064,7 @@ fn result_is_ok(symbol: Symbol, var_store: &mut VarStore) -> Def {
patterns: vec![no_region(pattern)],
value: no_region(true_expr),
guard: None,
redundant: RedundantMark::new(var_store),
};
branches.push(branch);
@ -5034,6 +5092,7 @@ fn result_is_ok(symbol: Symbol, var_store: &mut VarStore) -> Def {
patterns: vec![no_region(pattern)],
value: no_region(false_expr),
guard: None,
redundant: RedundantMark::new(var_store),
};
branches.push(branch);
@ -5046,6 +5105,7 @@ fn result_is_ok(symbol: Symbol, var_store: &mut VarStore) -> Def {
loc_cond: Box::new(no_region(Var(Symbol::ARG_1))),
branches,
branches_cond_var: var_store.fresh(),
exhaustive: ExhaustiveMark::new(var_store),
};
defn(
@ -5097,6 +5157,7 @@ fn result_after(symbol: Symbol, var_store: &mut VarStore) -> Def {
patterns: vec![no_region(pattern)],
value: no_region(ok),
guard: None,
redundant: RedundantMark::new(var_store),
};
branches.push(branch);
@ -5127,6 +5188,7 @@ fn result_after(symbol: Symbol, var_store: &mut VarStore) -> Def {
patterns: vec![no_region(pattern)],
value: no_region(err),
guard: None,
redundant: RedundantMark::new(var_store),
};
branches.push(branch);
@ -5139,6 +5201,7 @@ fn result_after(symbol: Symbol, var_store: &mut VarStore) -> Def {
loc_cond: Box::new(no_region(Var(Symbol::ARG_1))),
branches,
branches_cond_var: var_store.fresh(),
exhaustive: ExhaustiveMark::new(var_store),
};
defn(
@ -5324,7 +5387,13 @@ fn defn_help(
let closure_args = args
.into_iter()
.map(|(var, symbol)| (var, no_region(Identifier(symbol))))
.map(|(var, symbol)| {
(
var,
AnnotatedMark::new(var_store),
no_region(Identifier(symbol)),
)
})
.collect();
Closure(ClosureData {

View file

@ -4,7 +4,7 @@ use roc_collections::soa::{EitherIndex, Index, Slice};
use roc_module::ident::TagName;
use roc_module::symbol::{ModuleId, Symbol};
use roc_region::all::{Loc, Region};
use roc_types::subs::Variable;
use roc_types::subs::{ExhaustiveMark, Variable};
use roc_types::types::{Category, PatternCategory, Type};
#[derive(Debug)]
@ -22,6 +22,7 @@ pub struct Constraints {
pub strings: Vec<&'static str>,
pub sketched_rows: Vec<SketchedRows>,
pub eq: Vec<Eq>,
pub pattern_eq: Vec<PatternEq>,
}
impl Default for Constraints {
@ -45,6 +46,7 @@ impl Constraints {
let strings = Vec::new();
let sketched_rows = Vec::new();
let eq = Vec::new();
let pattern_eq = Vec::new();
types.extend([
Type::EmptyRec,
@ -97,6 +99,7 @@ impl Constraints {
strings,
sketched_rows,
eq,
pattern_eq,
}
}
@ -610,19 +613,35 @@ impl Constraints {
&mut self,
real_var: Variable,
real_region: Region,
real_category: Category,
expected_branches: Expected<Type>,
category_and_expectation: Result<
(Category, Expected<Type>),
(PatternCategory, PExpected<Type>),
>,
sketched_rows: SketchedRows,
context: ExhaustiveContext,
exhaustive: ExhaustiveMark,
) -> Constraint {
let real_var = Self::push_type_variable(real_var);
let real_category = Index::push_new(&mut self.categories, real_category);
let expected_branches = Index::push_new(&mut self.expectations, expected_branches);
let equality = Eq(real_var, expected_branches, real_category, real_region);
let equality = Index::push_new(&mut self.eq, equality);
let sketched_rows = Index::push_new(&mut self.sketched_rows, sketched_rows);
Constraint::Exhaustive(equality, sketched_rows, context)
let equality = match category_and_expectation {
Ok((category, expected)) => {
let category = Index::push_new(&mut self.categories, category);
let expected = Index::push_new(&mut self.expectations, expected);
let equality = Eq(real_var, expected, category, real_region);
let equality = Index::push_new(&mut self.eq, equality);
Ok(equality)
}
Err((category, expected)) => {
let category = Index::push_new(&mut self.pattern_categories, category);
let expected = Index::push_new(&mut self.pattern_expectations, expected);
let equality = PatternEq(real_var, expected, category, real_region);
let equality = Index::push_new(&mut self.pattern_eq, equality);
Err(equality)
}
};
Constraint::Exhaustive(equality, sketched_rows, context, exhaustive)
}
}
@ -637,6 +656,14 @@ pub struct Eq(
pub Region,
);
#[derive(Clone, Copy, Debug)]
pub struct PatternEq(
pub EitherIndex<Type, Variable>,
pub Index<PExpected<Type>>,
pub Index<PatternCategory>,
pub Region,
);
#[derive(Clone, Copy)]
pub enum Constraint {
Eq(Eq),
@ -672,7 +699,12 @@ pub enum Constraint {
Index<PatternCategory>,
Region,
),
Exhaustive(Index<Eq>, Index<SketchedRows>, ExhaustiveContext),
Exhaustive(
Result<Index<Eq>, Index<PatternEq>>,
Index<SketchedRows>,
ExhaustiveContext,
ExhaustiveMark,
),
}
#[derive(Debug, Clone, Copy, Default)]
@ -727,8 +759,12 @@ impl std::fmt::Debug for Constraint {
arg0, arg1, arg2, arg3
)
}
Self::Exhaustive(arg0, arg1, arg2) => {
write!(f, "Exhaustive({:?}, {:?}, {:?})", arg0, arg1, arg2)
Self::Exhaustive(arg0, arg1, arg2, arg3) => {
write!(
f,
"Exhaustive({:?}, {:?}, {:?}, {:?})",
arg0, arg1, arg2, arg3
)
}
}
}

View file

@ -2,6 +2,7 @@ use crate::abilities::MemberVariables;
use crate::annotation::canonicalize_annotation;
use crate::annotation::IntroducedVariables;
use crate::env::Env;
use crate::expr::AnnotatedMark;
use crate::expr::ClosureData;
use crate::expr::Expr::{self, *};
use crate::expr::{canonicalize_expr, Output, Recursive};
@ -1085,7 +1086,11 @@ fn canonicalize_pending_value_def<'a>(
region: Region::zero(),
};
underscores.push((var_store.fresh(), underscore));
underscores.push((
var_store.fresh(),
AnnotatedMark::known_exhaustive(),
underscore,
));
}
let body_expr = Loc {

View file

@ -1,6 +1,6 @@
use crate::annotation::IntroducedVariables;
use crate::def::{Declaration, Def};
use crate::expr::{ClosureData, Expr, Recursive};
use crate::expr::{AnnotatedMark, ClosureData, Expr, Recursive};
use crate::pattern::Pattern;
use crate::scope::Scope;
use roc_collections::{SendMap, VecSet};
@ -8,7 +8,7 @@ use roc_module::called_via::CalledVia;
use roc_module::ident::{Lowercase, TagName};
use roc_module::symbol::Symbol;
use roc_region::all::{Loc, Region};
use roc_types::subs::{VarStore, Variable};
use roc_types::subs::{ExhaustiveMark, RedundantMark, VarStore, Variable};
use roc_types::types::{AliasKind, LambdaSet, Type, TypeExtension};
#[derive(Debug, Default, Clone, Copy)]
@ -120,6 +120,7 @@ fn build_effect_always(
let const_closure = {
let arguments = vec![(
var_store.fresh(),
AnnotatedMark::new(var_store),
Loc::at_zero(empty_record_pattern(var_store)),
)];
@ -154,6 +155,7 @@ fn build_effect_always(
let arguments = vec![(
var_store.fresh(),
AnnotatedMark::new(var_store),
Loc::at_zero(Pattern::Identifier(value_symbol)),
)];
@ -277,6 +279,7 @@ fn build_effect_map(
let inner_closure = {
let arguments = vec![(
var_store.fresh(),
AnnotatedMark::new(var_store),
Loc::at_zero(empty_record_pattern(var_store)),
)];
@ -302,6 +305,7 @@ fn build_effect_map(
let arguments = vec![
(
var_store.fresh(),
AnnotatedMark::new(var_store),
Loc::at_zero(Pattern::UnwrappedOpaque {
opaque: effect_symbol,
whole_var: var_store.fresh(),
@ -316,6 +320,7 @@ fn build_effect_map(
),
(
var_store.fresh(),
AnnotatedMark::new(var_store),
Loc::at_zero(Pattern::Identifier(mapper_symbol)),
),
];
@ -466,6 +471,7 @@ fn build_effect_after(
let arguments = vec![
(
var_store.fresh(),
AnnotatedMark::new(var_store),
Loc::at_zero(Pattern::UnwrappedOpaque {
opaque: effect_symbol,
whole_var: var_store.fresh(),
@ -480,6 +486,7 @@ fn build_effect_after(
),
(
var_store.fresh(),
AnnotatedMark::new(var_store),
Loc::at_zero(Pattern::Identifier(to_effect_symbol)),
),
];
@ -579,6 +586,7 @@ fn wrap_in_effect_thunk(
let const_closure = {
let arguments = vec![(
var_store.fresh(),
AnnotatedMark::new(var_store),
Loc::at_zero(empty_record_pattern(var_store)),
)];
@ -720,7 +728,11 @@ fn build_effect_forever(
let body = build_effect_forever_body(scope, effect_symbol, forever_symbol, effect, var_store);
let arguments = vec![(var_store.fresh(), Loc::at_zero(Pattern::Identifier(effect)))];
let arguments = vec![(
var_store.fresh(),
AnnotatedMark::new(var_store),
Loc::at_zero(Pattern::Identifier(effect)),
)];
let function_var = var_store.fresh();
let after_closure = Expr::Closure(ClosureData {
@ -939,10 +951,12 @@ fn build_effect_loop(
let arguments = vec![
(
var_store.fresh(),
AnnotatedMark::new(var_store),
Loc::at_zero(Pattern::Identifier(state_symbol)),
),
(
var_store.fresh(),
AnnotatedMark::new(var_store),
Loc::at_zero(Pattern::Identifier(step_symbol)),
),
];
@ -1209,6 +1223,7 @@ fn build_effect_loop_inner_body(
patterns: vec![Loc::at_zero(step_pattern)],
value: Loc::at_zero(force_thunk2),
guard: None,
redundant: RedundantMark::new(var_store),
}
};
@ -1220,6 +1235,7 @@ fn build_effect_loop_inner_body(
patterns: vec![Loc::at_zero(done_pattern)],
value: Loc::at_zero(Expr::Var(done_symbol)),
guard: None,
redundant: RedundantMark::new(var_store),
}
};
@ -1232,6 +1248,7 @@ fn build_effect_loop_inner_body(
loc_cond: Box::new(force_thunk_call),
branches,
branches_cond_var: var_store.fresh(),
exhaustive: ExhaustiveMark::new(var_store),
};
Expr::LetNonRec(
@ -1254,7 +1271,7 @@ pub fn build_host_exposed_def(
let mut pattern_vars = SendMap::default();
pattern_vars.insert(symbol, expr_var);
let mut arguments: Vec<(Variable, Loc<Pattern>)> = Vec::new();
let mut arguments: Vec<(Variable, AnnotatedMark, Loc<Pattern>)> = Vec::new();
let mut linked_symbol_arguments: Vec<(Variable, Expr)> = Vec::new();
let mut captured_symbols: Vec<(Symbol, Variable)> = Vec::new();
@ -1278,7 +1295,11 @@ pub fn build_host_exposed_def(
let arg_var = var_store.fresh();
arguments.push((arg_var, Loc::at_zero(Pattern::Identifier(arg_symbol))));
arguments.push((
arg_var,
AnnotatedMark::new(var_store),
Loc::at_zero(Pattern::Identifier(arg_symbol)),
));
captured_symbols.push((arg_symbol, arg_var));
linked_symbol_arguments.push((arg_var, Expr::Var(arg_symbol)));
@ -1308,6 +1329,7 @@ pub fn build_host_exposed_def(
recursive: Recursive::NotRecursive,
arguments: vec![(
var_store.fresh(),
AnnotatedMark::new(var_store),
Loc::at_zero(empty_record_pattern(var_store)),
)],
loc_body: Box::new(Loc::at_zero(low_level_call)),
@ -1367,7 +1389,11 @@ pub fn build_host_exposed_def(
name: effect_closure_symbol,
captured_symbols,
recursive: Recursive::NotRecursive,
arguments: vec![(var_store.fresh(), Loc::at_zero(empty_record_pattern))],
arguments: vec![(
var_store.fresh(),
AnnotatedMark::new(var_store),
Loc::at_zero(empty_record_pattern),
)],
loc_body: Box::new(Loc::at_zero(low_level_call)),
});

View file

@ -7,7 +7,7 @@ use roc_exhaustive::{
};
use roc_module::ident::{TagIdIntType, TagName};
use roc_region::all::{Loc, Region};
use roc_types::subs::{Content, FlatType, Subs, SubsFmtContent, Variable};
use roc_types::subs::{Content, FlatType, RedundantMark, Subs, SubsFmtContent, Variable};
use roc_types::types::AliasKind;
pub use roc_exhaustive::Context as ExhaustiveContext;
@ -15,17 +15,40 @@ pub use roc_exhaustive::Context as ExhaustiveContext;
pub const GUARD_CTOR: &str = "#Guard";
pub const NONEXHAUSIVE_CTOR: &str = "#Open";
pub struct ExhaustiveSummary {
pub errors: Vec<Error>,
pub exhaustive: bool,
pub redundancies: Vec<RedundantMark>,
}
pub fn check(
subs: &Subs,
sketched_rows: SketchedRows,
context: ExhaustiveContext,
) -> Result<(), Vec<Error>> {
) -> ExhaustiveSummary {
let overall_region = sketched_rows.overall_region;
// TODO: can we keep going even if we had redundant rows?
let matrix = sketched_rows
.reify_to_non_redundant(subs)
.map_err(|e| vec![e])?;
roc_exhaustive::check(overall_region, context, matrix)
let mut all_errors = Vec::with_capacity(1);
let NonRedundantSummary {
non_redundant_rows,
errors,
redundancies,
} = sketched_rows.reify_to_non_redundant(subs);
all_errors.extend(errors);
let exhaustive = match roc_exhaustive::check(overall_region, context, non_redundant_rows) {
Ok(()) => true,
Err(errors) => {
all_errors.extend(errors);
false
}
};
ExhaustiveSummary {
errors: all_errors,
exhaustive,
redundancies,
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
@ -63,6 +86,7 @@ struct SketchedRow {
patterns: Vec<SketchedPattern>,
region: Region,
guard: Guard,
redundant_mark: RedundantMark,
}
#[derive(Clone, Debug, PartialEq, Eq)]
@ -72,7 +96,7 @@ pub struct SketchedRows {
}
impl SketchedRows {
pub fn reify_to_non_redundant(self, subs: &Subs) -> Result<Vec<Vec<Pattern>>, Error> {
fn reify_to_non_redundant(self, subs: &Subs) -> NonRedundantSummary {
to_nonredundant_rows(subs, self)
}
}
@ -173,7 +197,11 @@ fn sketch_pattern(var: Variable, pattern: &crate::pattern::Pattern) -> SketchedP
}
}
pub fn sketch_rows(target_var: Variable, region: Region, patterns: &[WhenBranch]) -> SketchedRows {
pub fn sketch_when_branches(
target_var: Variable,
region: Region,
patterns: &[WhenBranch],
) -> SketchedRows {
let mut rows: Vec<SketchedRow> = Vec::with_capacity(patterns.len());
// If any of the branches has a guard, e.g.
@ -197,6 +225,7 @@ pub fn sketch_rows(target_var: Variable, region: Region, patterns: &[WhenBranch]
patterns,
guard,
value: _,
redundant,
} in patterns
{
let guard = if guard.is_some() {
@ -242,6 +271,7 @@ pub fn sketch_rows(target_var: Variable, region: Region, patterns: &[WhenBranch]
patterns,
region: loc_pat.region,
guard,
redundant_mark: *redundant,
};
rows.push(row);
}
@ -253,20 +283,48 @@ pub fn sketch_rows(target_var: Variable, region: Region, patterns: &[WhenBranch]
}
}
pub fn sketch_pattern_to_rows(
target_var: Variable,
region: Region,
pattern: &crate::pattern::Pattern,
) -> SketchedRows {
let row = SketchedRow {
patterns: vec![sketch_pattern(target_var, pattern)],
region,
// A single row cannot be redundant!
redundant_mark: RedundantMark::known_non_redundant(),
guard: Guard::NoGuard,
};
SketchedRows {
rows: vec![row],
overall_region: region,
}
}
/// REDUNDANT PATTERNS
struct NonRedundantSummary {
non_redundant_rows: Vec<Vec<Pattern>>,
redundancies: Vec<RedundantMark>,
errors: Vec<Error>,
}
/// INVARIANT: Produces a list of rows where (forall row. length row == 1)
fn to_nonredundant_rows(subs: &Subs, rows: SketchedRows) -> Result<Vec<Vec<Pattern>>, Error> {
fn to_nonredundant_rows(subs: &Subs, rows: SketchedRows) -> NonRedundantSummary {
let SketchedRows {
rows,
overall_region,
} = rows;
let mut checked_rows = Vec::with_capacity(rows.len());
let mut redundancies = vec![];
let mut errors = vec![];
for SketchedRow {
patterns,
guard,
region,
redundant_mark,
} in rows.into_iter()
{
let next_row: Vec<Pattern> = patterns
@ -277,7 +335,8 @@ fn to_nonredundant_rows(subs: &Subs, rows: SketchedRows) -> Result<Vec<Vec<Patte
if matches!(guard, Guard::HasGuard) || is_useful(checked_rows.clone(), next_row.clone()) {
checked_rows.push(next_row);
} else {
return Err(Error::Redundant {
redundancies.push(redundant_mark);
errors.push(Error::Redundant {
overall_region,
branch_region: region,
index: HumanIndex::zero_based(checked_rows.len()),
@ -285,7 +344,11 @@ fn to_nonredundant_rows(subs: &Subs, rows: SketchedRows) -> Result<Vec<Vec<Patte
}
}
Ok(checked_rows)
NonRedundantSummary {
non_redundant_rows: checked_rows,
redundancies,
errors,
}
}
fn convert_tag(subs: &Subs, whole_var: Variable, this_tag: &TagName) -> (Union, TagId) {

View file

@ -18,7 +18,7 @@ use roc_parse::ast::{self, EscapedChar, StrLiteral};
use roc_parse::pattern::PatternType::*;
use roc_problem::can::{PrecedenceProblem, Problem, RuntimeError};
use roc_region::all::{Loc, Region};
use roc_types::subs::{VarStore, Variable};
use roc_types::subs::{ExhaustiveMark, RedundantMark, VarStore, Variable};
use roc_types::types::{Alias, Category, LambdaSet, Type};
use std::fmt::{Debug, Display};
use std::{char, u32};
@ -84,12 +84,18 @@ pub enum Expr {
Var(Symbol),
// Branching
When {
/// The actual condition of the when expression.
loc_cond: Box<Loc<Expr>>,
cond_var: Variable,
/// Result type produced by the branches.
expr_var: Variable,
region: Region,
loc_cond: Box<Loc<Expr>>,
/// The branches of the when, and the type of the condition that they expect to be matched
/// against.
branches: Vec<WhenBranch>,
branches_cond_var: Variable,
/// Whether the branches are exhaustive.
exhaustive: ExhaustiveMark,
},
If {
cond_var: Variable,
@ -235,6 +241,32 @@ impl Expr {
}
}
/// Stores exhaustiveness-checking metadata for a closure argument that may
/// have an annotated type.
#[derive(Clone, Copy, Debug)]
pub struct AnnotatedMark {
pub annotation_var: Variable,
pub exhaustive: ExhaustiveMark,
}
impl AnnotatedMark {
pub fn new(var_store: &mut VarStore) -> Self {
Self {
annotation_var: var_store.fresh(),
exhaustive: ExhaustiveMark::new(var_store),
}
}
// NOTE: only ever use this if you *know* a pattern match is surely exhaustive!
// Otherwise you will get unpleasant unification errors.
pub fn known_exhaustive() -> Self {
Self {
annotation_var: Variable::EMPTY_TAG_UNION,
exhaustive: ExhaustiveMark::known_exhaustive(),
}
}
}
#[derive(Clone, Debug)]
pub struct ClosureData {
pub function_type: Variable,
@ -244,7 +276,7 @@ pub struct ClosureData {
pub name: Symbol,
pub captured_symbols: Vec<(Symbol, Variable)>,
pub recursive: Recursive,
pub arguments: Vec<(Variable, Loc<Pattern>)>,
pub arguments: Vec<(Variable, AnnotatedMark, Loc<Pattern>)>,
pub loc_body: Box<Loc<Expr>>,
}
@ -296,7 +328,11 @@ impl AccessorData {
let loc_body = Loc::at_zero(body);
let arguments = vec![(record_var, Loc::at_zero(Pattern::Identifier(record_symbol)))];
let arguments = vec![(
record_var,
AnnotatedMark::known_exhaustive(),
Loc::at_zero(Pattern::Identifier(record_symbol)),
)];
ClosureData {
function_type: function_var,
@ -332,6 +368,8 @@ pub struct WhenBranch {
pub patterns: Vec<Loc<Pattern>>,
pub value: Loc<Expr>,
pub guard: Option<Loc<Expr>>,
/// Whether this branch is redundant in the `when` it appears in
pub redundant: RedundantMark,
}
impl WhenBranch {
@ -711,6 +749,7 @@ pub fn canonicalize_expr<'a>(
loc_cond: Box::new(can_cond),
branches: can_branches,
branches_cond_var: var_store.fresh(),
exhaustive: ExhaustiveMark::new(var_store),
};
(expr, output)
@ -992,11 +1031,15 @@ fn canonicalize_closure_body<'a>(
loc_pattern.region,
);
can_args.push((var_store.fresh(), can_argument_pattern));
can_args.push((
var_store.fresh(),
AnnotatedMark::new(var_store),
can_argument_pattern,
));
}
let bound_by_argument_patterns: Vec<_> =
BindingsFromPattern::new_many(can_args.iter().map(|x| &x.1)).collect();
BindingsFromPattern::new_many(can_args.iter().map(|x| &x.2)).collect();
let (loc_body_expr, new_output) = canonicalize_expr(
env,
@ -1130,6 +1173,7 @@ fn canonicalize_when_branch<'a>(
patterns,
value,
guard,
redundant: RedundantMark::new(var_store),
},
references,
)
@ -1342,6 +1386,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
loc_cond,
branches,
branches_cond_var,
exhaustive,
} => {
let loc_cond = Box::new(Loc {
region: loc_cond.region,
@ -1366,6 +1411,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
patterns: branch.patterns,
value,
guard,
redundant: RedundantMark::new(var_store),
};
new_branches.push(new_branch);
@ -1378,6 +1424,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
loc_cond,
branches: new_branches,
branches_cond_var,
exhaustive,
}
}
If {
@ -1607,7 +1654,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
// Wrap the body in one LetNonRec for each argument,
// such that at the end we have all the arguments in
// scope with the values the caller provided.
for ((_param_var, loc_pattern), (expr_var, loc_expr)) in
for ((_param_var, _exhaustive_mark, loc_pattern), (expr_var, loc_expr)) in
params.iter().cloned().zip(args.into_iter()).rev()
{
// TODO get the correct vars into here.

View file

@ -663,7 +663,7 @@ fn fix_values_captured_in_closure_expr(
}
// patterns can contain default expressions, so much go over them too!
for (_, loc_pat) in arguments.iter_mut() {
for (_, _, loc_pat) in arguments.iter_mut() {
fix_values_captured_in_closure_pattern(&mut loc_pat.value, no_capture_symbols);
}

View file

@ -13,7 +13,7 @@ use roc_parse::pattern::PatternType;
use roc_problem::can::{MalformedPatternProblem, Problem, RuntimeError, ShadowKind};
use roc_region::all::{Loc, Region};
use roc_types::subs::{VarStore, Variable};
use roc_types::types::{LambdaSet, Type};
use roc_types::types::{LambdaSet, PatternCategory, Type};
/// A pattern, including possible problems (e.g. shadowing) so that
/// codegen can generate a runtime error if this pattern is reached.
@ -105,6 +105,59 @@ impl Pattern {
}
}
}
/// Is this pattern sure to cover all instances of a type T, assuming it typechecks against T?
pub fn surely_exhaustive(&self) -> bool {
use Pattern::*;
match self {
Identifier(..)
| Underscore
| Shadowed(..)
| OpaqueNotInScope(..)
| UnsupportedPattern(..)
| MalformedPattern(..)
| AbilityMemberSpecialization { .. } => true,
RecordDestructure { destructs, .. } => destructs.is_empty(),
AppliedTag { .. }
| NumLiteral(..)
| IntLiteral(..)
| FloatLiteral(..)
| StrLiteral(..)
| SingleQuote(..) => false,
UnwrappedOpaque { argument, .. } => {
// Opaques can only match against one constructor (the opaque symbol), so this is
// surely exhaustive against T if the inner pattern is surely exhaustive against
// its type U.
argument.1.value.surely_exhaustive()
}
}
}
pub fn category(&self) -> PatternCategory {
use Pattern::*;
use PatternCategory as C;
match self {
Identifier(_) => C::PatternDefault,
AppliedTag { tag_name, .. } => C::Ctor(tag_name.clone()),
UnwrappedOpaque { opaque, .. } => C::Opaque(*opaque),
RecordDestructure { destructs, .. } if destructs.is_empty() => C::EmptyRecord,
RecordDestructure { .. } => C::Record,
NumLiteral(..) => C::Num,
IntLiteral(..) => C::Int,
FloatLiteral(..) => C::Float,
StrLiteral(_) => C::Str,
SingleQuote(_) => C::Character,
Underscore => C::PatternDefault,
AbilityMemberSpecialization { .. } => C::PatternDefault,
Shadowed(..) | OpaqueNotInScope(..) | UnsupportedPattern(..) | MalformedPattern(..) => {
C::PatternDefault
}
}
}
}
#[derive(Clone, Debug)]

View file

@ -66,6 +66,7 @@ fn walk_expr<V: Visitor>(visitor: &mut V, expr: &Expr) {
branches,
region: _,
branches_cond_var: _,
exhaustive: _,
} => {
walk_when(visitor, *cond_var, *expr_var, loc_cond, branches);
}
@ -81,9 +82,9 @@ fn walk_closure<V: Visitor>(visitor: &mut V, clos: &ClosureData) {
..
} = clos;
arguments
.iter()
.for_each(|(var, arg)| visitor.visit_pattern(&arg.value, arg.region, Some(*var)));
arguments.iter().for_each(|(var, _exhaustive_mark, arg)| {
visitor.visit_pattern(&arg.value, arg.region, Some(*var))
});
visitor.visit_expr(&loc_body.value, loc_body.region, *return_type);
}
@ -107,6 +108,7 @@ fn walk_when_branch<V: Visitor>(visitor: &mut V, branch: &WhenBranch, expr_var:
patterns,
value,
guard,
redundant: _,
} = branch;
patterns