Move exhaustiveness checking to type checking

This commit is contained in:
Ayaz Hafiz 2022-04-22 15:23:56 -04:00
parent f7e04490c0
commit 85e3373d8b
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
15 changed files with 346 additions and 94 deletions

View file

@ -4727,6 +4727,7 @@ fn result_map(symbol: Symbol, var_store: &mut VarStore) -> Def {
region: Region::zero(),
loc_cond: Box::new(no_region(Var(Symbol::ARG_1))),
branches,
branches_cond_var: var_store.fresh(),
};
defn(
@ -4824,6 +4825,7 @@ fn result_map_err(symbol: Symbol, var_store: &mut VarStore) -> Def {
region: Region::zero(),
loc_cond: Box::new(no_region(Var(Symbol::ARG_1))),
branches,
branches_cond_var: var_store.fresh(),
};
defn(
@ -4887,6 +4889,7 @@ fn result_with_default(symbol: Symbol, var_store: &mut VarStore) -> Def {
region: Region::zero(),
loc_cond: Box::new(no_region(Var(Symbol::ARG_1))),
branches,
branches_cond_var: var_store.fresh(),
};
defn(
@ -4964,6 +4967,7 @@ fn result_is_err(symbol: Symbol, var_store: &mut VarStore) -> Def {
region: Region::zero(),
loc_cond: Box::new(no_region(Var(Symbol::ARG_1))),
branches,
branches_cond_var: var_store.fresh(),
};
defn(
@ -5041,6 +5045,7 @@ fn result_is_ok(symbol: Symbol, var_store: &mut VarStore) -> Def {
region: Region::zero(),
loc_cond: Box::new(no_region(Var(Symbol::ARG_1))),
branches,
branches_cond_var: var_store.fresh(),
};
defn(
@ -5133,6 +5138,7 @@ fn result_after(symbol: Symbol, var_store: &mut VarStore) -> Def {
region: Region::zero(),
loc_cond: Box::new(no_region(Var(Symbol::ARG_1))),
branches,
branches_cond_var: var_store.fresh(),
};
defn(

View file

@ -574,7 +574,7 @@ impl Constraints {
Constraint::IsOpenType(_) => false,
Constraint::IncludesTag(_) => false,
Constraint::PatternPresence(_, _, _, _) => false,
Constraint::Exhaustive(_, _) => false,
Constraint::Exhaustive { .. } => false,
}
}
@ -603,10 +603,27 @@ impl Constraints {
Constraint::Store(type_index, variable, string_index, line_number)
}
pub fn exhaustive(&mut self, rows: SketchedRows, context: ExhaustiveContext) -> Constraint {
let rows_index = Index::push_new(&mut self.sketched_rows, rows);
pub fn exhaustive(
&mut self,
real_var: Variable,
real_region: Region,
real_category: Category,
expected_branches: Expected<Type>,
sketched_rows: SketchedRows,
context: ExhaustiveContext,
) -> Constraint {
let real_category = Index::push_new(&mut self.categories, real_category);
let expected_branches = Index::push_new(&mut self.expectations, expected_branches);
let sketched_rows = Index::push_new(&mut self.sketched_rows, sketched_rows);
Constraint::Exhaustive(rows_index, context)
Constraint::Exhaustive {
real_var,
real_region,
real_category,
expected_branches,
sketched_rows,
context,
}
}
}
@ -652,7 +669,14 @@ pub enum Constraint {
Index<PatternCategory>,
Region,
),
Exhaustive(Index<SketchedRows>, ExhaustiveContext),
Exhaustive {
real_var: Variable,
real_region: Region,
real_category: Index<Category>,
expected_branches: Index<Expected<Type>>,
sketched_rows: Index<SketchedRows>,
context: ExhaustiveContext,
},
}
#[derive(Debug, Clone, Copy, Default)]
@ -707,8 +731,19 @@ impl std::fmt::Debug for Constraint {
arg0, arg1, arg2, arg3
)
}
Self::Exhaustive(arg0, arg1) => {
write!(f, "Exhaustive({:?}, {:?})", arg0, arg1)
Self::Exhaustive {
real_var: arg0,
real_region: arg1,
real_category: arg2,
expected_branches: arg3,
sketched_rows: arg4,
context: arg5,
} => {
write!(
f,
"Exhaustive({:?}, {:?}, {:?}, {:?}, {:?}, {:?})",
arg0, arg1, arg2, arg3, arg4, arg5
)
}
}
}

View file

@ -1366,6 +1366,7 @@ fn build_effect_loop_inner_body(
region: Region::zero(),
loc_cond: Box::new(force_thunk_call),
branches,
branches_cond_var: var_store.fresh(),
};
Expr::LetNonRec(

View file

@ -6,6 +6,7 @@ use roc_exhaustive::{is_useful, Ctor, Error, Guard, Literal, Pattern, RenderAs,
use roc_module::ident::{TagIdIntType, TagName};
use roc_region::all::{Loc, Region};
use roc_types::subs::{Content, FlatType, Subs, SubsFmtContent, Variable};
use roc_types::types::AliasKind;
pub use roc_exhaustive::Context as ExhaustiveContext;
@ -22,7 +23,7 @@ pub fn check(
roc_exhaustive::check(overall_region, context, matrix)
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, Eq)]
enum SketchedPattern {
Anything,
Literal(Literal),
@ -52,14 +53,14 @@ impl SketchedPattern {
}
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, Eq)]
struct SketchedRow {
patterns: Vec<SketchedPattern>,
region: Region,
guard: Guard,
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SketchedRows {
rows: Vec<SketchedRow>,
overall_region: Region,
@ -155,13 +156,15 @@ fn sketch_pattern(var: Variable, pattern: &crate::pattern::Pattern) -> SketchedP
)
}
// Treat this like a literal so we mark it as non-exhaustive
MalformedPattern(..) => SP::Literal(Literal::Byte(1)),
Underscore
| Identifier(_)
| AbilityMemberSpecialization { .. }
| Shadowed(..)
| OpaqueNotInScope(..)
| UnsupportedPattern(..)
| MalformedPattern(..) => SP::Anything,
| UnsupportedPattern(..) => SP::Anything,
}
}
@ -232,7 +235,7 @@ pub fn sketch_rows(target_var: Variable, region: Region, patterns: &[WhenBranch]
let row = SketchedRow {
patterns,
region,
region: loc_pat.region,
guard,
};
rows.push(row);
@ -285,7 +288,7 @@ fn convert_tag(subs: &Subs, whole_var: Variable, this_tag: &TagName) -> (Union,
use {Content::*, FlatType::*};
match content {
match dealias_tag(subs, content) {
Structure(TagUnion(tags, ext) | RecursiveTagUnion(_, tags, ext)) => {
let (sorted_tags, ext) = tags.sorted_iterator_and_ext(subs, *ext);
@ -335,3 +338,18 @@ fn convert_tag(subs: &Subs, whole_var: Variable, this_tag: &TagName) -> (Union,
),
}
}
pub fn dealias_tag<'a>(subs: &'a Subs, content: &'a Content) -> &'a Content {
use Content::*;
let mut result = content;
loop {
match result {
Alias(_, _, real_var, AliasKind::Structural)
| RecursionVar {
structure: real_var,
..
} => result = subs.get_content_without_compacting(*real_var),
_ => return result,
}
}
}

View file

@ -19,7 +19,7 @@ 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::types::{Alias, LambdaSet, Type};
use roc_types::types::{Alias, Category, LambdaSet, Type};
use std::fmt::{Debug, Display};
use std::{char, u32};
@ -89,6 +89,7 @@ pub enum Expr {
region: Region,
loc_cond: Box<Loc<Expr>>,
branches: Vec<WhenBranch>,
branches_cond_var: Variable,
},
If {
cond_var: Variable,
@ -193,6 +194,47 @@ pub enum Expr {
// Compiles, but will crash if reached
RuntimeError(RuntimeError),
}
impl Expr {
pub fn category(&self) -> Category {
match self {
Self::Num(..) => Category::Num,
Self::Int(..) => Category::Int,
Self::Float(..) => Category::Float,
Self::Str(..) => Category::Str,
Self::SingleQuote(..) => Category::Character,
Self::List { .. } => Category::List,
&Self::Var(sym) => Category::Lookup(sym),
Self::When { .. } => Category::When,
Self::If { .. } => Category::If,
Self::LetRec(_, expr, _) => expr.value.category(),
Self::LetNonRec(_, expr, _) => expr.value.category(),
&Self::Call(_, _, called_via) => Category::CallResult(None, called_via),
&Self::RunLowLevel { op, .. } => Category::LowLevelOpResult(op),
Self::ForeignCall { .. } => Category::ForeignCall,
Self::Closure(..) => Category::Lambda,
Self::Record { .. } => Category::Record,
Self::EmptyRecord => Category::Record,
Self::Access { field, .. } => Category::Access(field.clone()),
Self::Accessor(data) => Category::Accessor(data.field.clone()),
Self::Update { .. } => Category::Record,
Self::Tag {
name, arguments, ..
} => Category::TagApply {
tag_name: name.clone(),
args_count: arguments.len(),
},
Self::ZeroArgumentTag { name, .. } => Category::TagApply {
tag_name: name.clone(),
args_count: 0,
},
&Self::OpaqueRef { name, .. } => Category::OpaqueWrap(name),
Self::Expect(..) => Category::Expect,
Self::RuntimeError(..) => Category::Unknown,
}
}
}
#[derive(Clone, Debug)]
pub struct ClosureData {
pub function_type: Variable,
@ -782,6 +824,7 @@ pub fn canonicalize_expr<'a>(
region,
loc_cond: Box::new(can_cond),
branches: can_branches,
branches_cond_var: var_store.fresh(),
};
(expr, output)
@ -1298,6 +1341,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
region,
loc_cond,
branches,
branches_cond_var,
} => {
let loc_cond = Box::new(Loc {
region: loc_cond.region,
@ -1333,6 +1377,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
region,
loc_cond,
branches: new_branches,
branches_cond_var,
}
}
If {

View file

@ -65,6 +65,7 @@ fn walk_expr<V: Visitor>(visitor: &mut V, expr: &Expr) {
loc_cond,
branches,
region: _,
branches_cond_var: _,
} => {
walk_when(visitor, *cond_var, *expr_var, loc_cond, branches);
}