mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 06:14:46 +00:00
Redundant and exhaustive marks in mono
This commit is contained in:
parent
98869b557d
commit
2f1306afd1
10 changed files with 152 additions and 86 deletions
|
@ -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::{ExhaustiveMark, VarStore, Variable};
|
use roc_types::subs::{ExhaustiveMark, RedundantMark, VarStore, Variable};
|
||||||
|
|
||||||
macro_rules! macro_magic {
|
macro_rules! macro_magic {
|
||||||
(@single $($x:tt)*) => (());
|
(@single $($x:tt)*) => (());
|
||||||
|
@ -4686,6 +4686,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(var_store.fresh()),
|
||||||
};
|
};
|
||||||
|
|
||||||
branches.push(branch);
|
branches.push(branch);
|
||||||
|
@ -4716,6 +4717,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(var_store.fresh()),
|
||||||
};
|
};
|
||||||
|
|
||||||
branches.push(branch);
|
branches.push(branch);
|
||||||
|
@ -4785,6 +4787,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(var_store.fresh()),
|
||||||
};
|
};
|
||||||
|
|
||||||
branches.push(branch);
|
branches.push(branch);
|
||||||
|
@ -4815,6 +4818,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(var_store.fresh()),
|
||||||
};
|
};
|
||||||
|
|
||||||
branches.push(branch);
|
branches.push(branch);
|
||||||
|
@ -4860,6 +4864,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(var_store.fresh()),
|
||||||
};
|
};
|
||||||
|
|
||||||
branches.push(branch);
|
branches.push(branch);
|
||||||
|
@ -4880,6 +4885,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(var_store.fresh()),
|
||||||
};
|
};
|
||||||
|
|
||||||
branches.push(branch);
|
branches.push(branch);
|
||||||
|
@ -4932,6 +4938,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(var_store.fresh()),
|
||||||
};
|
};
|
||||||
|
|
||||||
branches.push(branch);
|
branches.push(branch);
|
||||||
|
@ -4959,6 +4966,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(var_store.fresh()),
|
||||||
};
|
};
|
||||||
|
|
||||||
branches.push(branch);
|
branches.push(branch);
|
||||||
|
@ -5011,6 +5019,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(var_store.fresh()),
|
||||||
};
|
};
|
||||||
|
|
||||||
branches.push(branch);
|
branches.push(branch);
|
||||||
|
@ -5038,6 +5047,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(var_store.fresh()),
|
||||||
};
|
};
|
||||||
|
|
||||||
branches.push(branch);
|
branches.push(branch);
|
||||||
|
@ -5102,6 +5112,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(var_store.fresh()),
|
||||||
};
|
};
|
||||||
|
|
||||||
branches.push(branch);
|
branches.push(branch);
|
||||||
|
@ -5132,6 +5143,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(var_store.fresh()),
|
||||||
};
|
};
|
||||||
|
|
||||||
branches.push(branch);
|
branches.push(branch);
|
||||||
|
|
|
@ -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::{ExhaustiveMark, 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)]
|
||||||
|
@ -1209,6 +1209,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(var_store.fresh()),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1220,6 +1221,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(var_store.fresh()),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -15,23 +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 ErrorSummary {
|
pub struct ExhaustiveSummary {
|
||||||
errors: Vec<Error>,
|
pub errors: Vec<Error>,
|
||||||
exhaustive: bool,
|
pub exhaustive: bool,
|
||||||
redundant: Vec<RedundantMark>,
|
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)]
|
||||||
|
@ -69,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)]
|
||||||
|
@ -78,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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -203,6 +221,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() {
|
||||||
|
@ -248,6 +267,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);
|
||||||
}
|
}
|
||||||
|
@ -261,18 +281,28 @@ pub fn sketch_rows(target_var: Variable, region: Region, patterns: &[WhenBranch]
|
||||||
|
|
||||||
/// 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
|
||||||
|
@ -283,7 +313,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()),
|
||||||
|
@ -291,7 +322,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::{ExhaustiveMark, 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};
|
||||||
|
@ -338,6 +338,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 {
|
||||||
|
@ -1137,6 +1139,7 @@ fn canonicalize_when_branch<'a>(
|
||||||
patterns,
|
patterns,
|
||||||
value,
|
value,
|
||||||
guard,
|
guard,
|
||||||
|
redundant: RedundantMark(var_store.fresh()),
|
||||||
},
|
},
|
||||||
references,
|
references,
|
||||||
)
|
)
|
||||||
|
@ -1374,6 +1377,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(var_store.fresh()),
|
||||||
};
|
};
|
||||||
|
|
||||||
new_branches.push(new_branch);
|
new_branches.push(new_branch);
|
||||||
|
|
|
@ -108,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
|
||||||
|
|
|
@ -15,7 +15,7 @@ use roc_collections::all::{HumanIndex, MutMap, SendMap};
|
||||||
use roc_module::ident::{Lowercase, TagName};
|
use roc_module::ident::{Lowercase, 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::{ExhaustiveMark, Variable};
|
use roc_types::subs::{ExhaustiveMark, RedundantMark, Variable};
|
||||||
use roc_types::types::Type::{self, *};
|
use roc_types::types::Type::{self, *};
|
||||||
use roc_types::types::{
|
use roc_types::types::{
|
||||||
AliasKind, AnnotationSource, Category, PReason, Reason, RecordField, TypeExtension,
|
AliasKind, AnnotationSource, Category, PReason, Reason, RecordField, TypeExtension,
|
||||||
|
@ -1177,11 +1177,20 @@ 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(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Mark the branch as non-redundant initially. We'll refine this during solving.
|
||||||
|
state.vars.push(when_branch.redundant.0);
|
||||||
|
state.constraints.push(constraints.store(
|
||||||
|
RedundantMark::NON_REDUNDANT_TYPE,
|
||||||
|
when_branch.redundant.0,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
));
|
||||||
|
|
||||||
// TODO investigate for error messages, is it better to unify all branches with a variable,
|
// TODO investigate for error messages, is it better to unify all branches with a variable,
|
||||||
// then unify that variable with the expectation?
|
// then unify that variable with the expectation?
|
||||||
for (i, loc_pattern) in when_branch.patterns.iter().enumerate() {
|
for (i, loc_pattern) in when_branch.patterns.iter().enumerate() {
|
||||||
|
|
|
@ -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};
|
||||||
|
|
||||||
|
@ -2090,8 +2093,12 @@ fn pattern_to_when<'a>(
|
||||||
patterns: vec![pattern],
|
patterns: vec![pattern],
|
||||||
value: body,
|
value: body,
|
||||||
guard: None,
|
guard: None,
|
||||||
|
// TODO(exhaustive): should come from real var!
|
||||||
|
redundant: RedundantMark(Variable::EMPTY_TAG_UNION),
|
||||||
}],
|
}],
|
||||||
branches_cond_var: pattern_var,
|
branches_cond_var: pattern_var,
|
||||||
|
// TODO(exhaustive): should come from real var!
|
||||||
|
exhaustive: ExhaustiveMark(Variable::EMPTY_TAG_UNION),
|
||||||
};
|
};
|
||||||
|
|
||||||
(symbol, Loc::at_zero(wrapped_body))
|
(symbol, Loc::at_zero(wrapped_body))
|
||||||
|
@ -3751,10 +3758,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 +3772,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),
|
||||||
|
@ -5442,10 +5450,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 +5462,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,
|
||||||
|
@ -5822,8 +5831,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 +5851,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,46 +5905,15 @@ fn to_opt_branches<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE exhaustiveness is checked after the construction of all the branches
|
if !exhaustive_mark.is_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;
|
|
||||||
match crate::exhaustive::check(region, &loc_branches, context) {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(errors) => {
|
|
||||||
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((
|
opt_branches.push((
|
||||||
Pattern::Underscore,
|
Pattern::Underscore,
|
||||||
None,
|
None,
|
||||||
roc_can::expr::Expr::RuntimeError(
|
roc_can::expr::Expr::RuntimeError(roc_problem::can::RuntimeError::NonExhaustivePattern),
|
||||||
roc_problem::can::RuntimeError::NonExhaustivePattern,
|
|
||||||
),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
opt_branches
|
opt_branches
|
||||||
}
|
}
|
||||||
|
@ -5940,9 +5923,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 +5935,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));
|
||||||
|
|
|
@ -12,7 +12,8 @@ use roc_region::all::{Loc, Region};
|
||||||
use roc_types::solved_types::Solved;
|
use roc_types::solved_types::Solved;
|
||||||
use roc_types::subs::{
|
use roc_types::subs::{
|
||||||
AliasVariables, Content, Descriptor, ExhaustiveMark, FlatType, Mark, OptVariable, Rank,
|
AliasVariables, Content, Descriptor, ExhaustiveMark, FlatType, Mark, OptVariable, Rank,
|
||||||
RecordFields, Subs, SubsIndex, SubsSlice, UnionTags, Variable, VariableSubsSlice,
|
RecordFields, RedundantMark, Subs, SubsIndex, SubsSlice, UnionTags, Variable,
|
||||||
|
VariableSubsSlice,
|
||||||
};
|
};
|
||||||
use roc_types::types::Type::{self, *};
|
use roc_types::types::Type::{self, *};
|
||||||
use roc_types::types::{
|
use roc_types::types::{
|
||||||
|
@ -1286,24 +1287,27 @@ 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 {
|
||||||
use roc_can::exhaustive::check;
|
use roc_can::exhaustive::{check, ExhaustiveSummary};
|
||||||
use roc_exhaustive::Error;
|
|
||||||
|
|
||||||
let checked = roc_can::exhaustive::check(subs, sketched_rows, context);
|
let ExhaustiveSummary {
|
||||||
if let Err(errors) = checked {
|
errors,
|
||||||
for error in errors.iter() {
|
exhaustive,
|
||||||
match error {
|
redundancies,
|
||||||
Error::Incomplete(..) => subs
|
} = check(subs, sketched_rows, context);
|
||||||
.set_content(exhaustive_mark.0, ExhaustiveMark::NON_EXHAUSTIVE),
|
|
||||||
Error::Redundant { index, .. } => {
|
// 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 {
|
||||||
|
subs.set_content(exhaustive_mark.0, ExhaustiveMark::NON_EXHAUSTIVE)
|
||||||
}
|
}
|
||||||
|
for redundant_mark in redundancies {
|
||||||
|
subs.set_content(redundant_mark.0, RedundantMark::REDUNDANT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Store the errors.
|
||||||
problems.extend(errors.into_iter().map(TypeError::Exhaustive));
|
problems.extend(errors.into_iter().map(TypeError::Exhaustive));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
|
|
|
@ -973,14 +973,27 @@ impl ExhaustiveMark {
|
||||||
pub const EXHAUSTIVE_TYPE: Type = Type::EmptyTagUnion;
|
pub const EXHAUSTIVE_TYPE: Type = Type::EmptyTagUnion;
|
||||||
pub const EXHAUSTIVE: Content = Content::Structure(FlatType::EmptyTagUnion);
|
pub const EXHAUSTIVE: Content = Content::Structure(FlatType::EmptyTagUnion);
|
||||||
pub const NON_EXHAUSTIVE: Content = Content::Error;
|
pub const NON_EXHAUSTIVE: Content = Content::Error;
|
||||||
|
|
||||||
|
pub fn is_exhaustive(&self, subs: &Subs) -> bool {
|
||||||
|
matches!(
|
||||||
|
subs.get_content_without_compacting(self.0),
|
||||||
|
Content::Structure(FlatType::EmptyTagUnion)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Marks whether a when branch is redundant using a variable.
|
/// Marks whether a when branch is redundant using a variable.
|
||||||
pub struct RedundantMark(Variable);
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub struct RedundantMark(pub Variable);
|
||||||
|
|
||||||
impl RedundantMark {
|
impl RedundantMark {
|
||||||
pub const REDUNDANT: Content = Content::Structure(FlatType::EmptyTagUnion);
|
pub const REDUNDANT: Content = Content::Error;
|
||||||
pub const NON_REDUNDANT: Content = Content::Error;
|
pub const NON_REDUNDANT: Content = Content::Structure(FlatType::EmptyTagUnion);
|
||||||
|
pub const NON_REDUNDANT_TYPE: Type = Type::EmptyTagUnion;
|
||||||
|
|
||||||
|
pub fn is_redundant(&self, subs: &Subs) -> bool {
|
||||||
|
matches!(subs.get_content_without_compacting(self.0), Content::Error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
|
|
@ -2508,7 +2508,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
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue