mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 14:54:47 +00:00
Merge pull request #2604 from rtfeldman/early-exhaustiveness-checking
Move out exhaustiveness checking to its own crate
This commit is contained in:
commit
f30c07edd0
11 changed files with 508 additions and 459 deletions
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -3502,6 +3502,16 @@ dependencies = [
|
||||||
name = "roc_error_macros"
|
name = "roc_error_macros"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "roc_exhaustive"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"roc_collections",
|
||||||
|
"roc_module",
|
||||||
|
"roc_region",
|
||||||
|
"roc_std",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "roc_fmt"
|
name = "roc_fmt"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -3651,6 +3661,7 @@ dependencies = [
|
||||||
"roc_can",
|
"roc_can",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
"roc_error_macros",
|
"roc_error_macros",
|
||||||
|
"roc_exhaustive",
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_problem",
|
"roc_problem",
|
||||||
"roc_region",
|
"roc_region",
|
||||||
|
@ -3772,6 +3783,7 @@ dependencies = [
|
||||||
"roc_can",
|
"roc_can",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
"roc_constrain",
|
"roc_constrain",
|
||||||
|
"roc_exhaustive",
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_mono",
|
"roc_mono",
|
||||||
"roc_parse",
|
"roc_parse",
|
||||||
|
|
|
@ -3,6 +3,7 @@ members = [
|
||||||
"compiler/ident",
|
"compiler/ident",
|
||||||
"compiler/region",
|
"compiler/region",
|
||||||
"compiler/collections",
|
"compiler/collections",
|
||||||
|
"compiler/exhaustive",
|
||||||
"compiler/module",
|
"compiler/module",
|
||||||
"compiler/parse",
|
"compiler/parse",
|
||||||
"compiler/can",
|
"compiler/can",
|
||||||
|
|
12
compiler/exhaustive/Cargo.toml
Normal file
12
compiler/exhaustive/Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "roc_exhaustive"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["The Roc Contributors"]
|
||||||
|
license = "UPL-1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
roc_collections = { path = "../collections" }
|
||||||
|
roc_region = { path = "../region" }
|
||||||
|
roc_module = { path = "../module" }
|
||||||
|
roc_std = { path = "../../roc_std", default-features = false }
|
440
compiler/exhaustive/src/lib.rs
Normal file
440
compiler/exhaustive/src/lib.rs
Normal file
|
@ -0,0 +1,440 @@
|
||||||
|
use roc_collections::all::{Index, MutMap};
|
||||||
|
use roc_module::ident::{Lowercase, TagIdIntType, TagName};
|
||||||
|
use roc_region::all::Region;
|
||||||
|
use roc_std::RocDec;
|
||||||
|
|
||||||
|
use self::Pattern::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Union {
|
||||||
|
pub alternatives: Vec<Ctor>,
|
||||||
|
pub render_as: RenderAs,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Union {
|
||||||
|
pub fn newtype_wrapper(tag_name: TagName, arity: usize) -> Self {
|
||||||
|
let alternatives = vec![Ctor {
|
||||||
|
name: tag_name,
|
||||||
|
tag_id: TagId(0),
|
||||||
|
arity,
|
||||||
|
}];
|
||||||
|
|
||||||
|
Union {
|
||||||
|
alternatives,
|
||||||
|
render_as: RenderAs::Tag,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum RenderAs {
|
||||||
|
Tag,
|
||||||
|
Opaque,
|
||||||
|
Record(Vec<Lowercase>),
|
||||||
|
Guard,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, Copy)]
|
||||||
|
pub struct TagId(pub TagIdIntType);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Ctor {
|
||||||
|
pub name: TagName,
|
||||||
|
pub tag_id: TagId,
|
||||||
|
pub arity: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum Pattern {
|
||||||
|
Anything,
|
||||||
|
Literal(Literal),
|
||||||
|
Ctor(Union, TagId, std::vec::Vec<Pattern>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum Literal {
|
||||||
|
Int(i128),
|
||||||
|
U128(u128),
|
||||||
|
Bit(bool),
|
||||||
|
Byte(u8),
|
||||||
|
Float(u64),
|
||||||
|
Decimal(RocDec),
|
||||||
|
Str(Box<str>),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Error
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum Error {
|
||||||
|
Incomplete(Region, Context, Vec<Pattern>),
|
||||||
|
Redundant {
|
||||||
|
overall_region: Region,
|
||||||
|
branch_region: Region,
|
||||||
|
index: Index,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum Context {
|
||||||
|
BadArg,
|
||||||
|
BadDestruct,
|
||||||
|
BadCase,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum Guard {
|
||||||
|
HasGuard,
|
||||||
|
NoGuard,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check
|
||||||
|
|
||||||
|
pub fn check(
|
||||||
|
region: Region,
|
||||||
|
context: Context,
|
||||||
|
matrix: Vec<Vec<Pattern>>,
|
||||||
|
) -> Result<(), Vec<Error>> {
|
||||||
|
let mut errors = Vec::new();
|
||||||
|
let bad_patterns = is_exhaustive(&matrix, 1);
|
||||||
|
if !bad_patterns.is_empty() {
|
||||||
|
// TODO i suspect this is like a concat in in practice? code below can panic
|
||||||
|
// if this debug_assert! ever fails, the theory is disproven
|
||||||
|
debug_assert!(bad_patterns.iter().map(|v| v.len()).sum::<usize>() == bad_patterns.len());
|
||||||
|
let heads = bad_patterns.into_iter().map(|mut v| v.remove(0)).collect();
|
||||||
|
errors.push(Error::Incomplete(region, context, heads));
|
||||||
|
return Err(errors);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// EXHAUSTIVE PATTERNS
|
||||||
|
|
||||||
|
/// INVARIANTS:
|
||||||
|
///
|
||||||
|
/// The initial rows "matrix" are all of length 1
|
||||||
|
/// The initial count of items per row "n" is also 1
|
||||||
|
/// The resulting rows are examples of missing patterns
|
||||||
|
fn is_exhaustive(matrix: &RefPatternMatrix, n: usize) -> PatternMatrix {
|
||||||
|
if matrix.is_empty() {
|
||||||
|
vec![std::iter::repeat(Anything).take(n).collect()]
|
||||||
|
} else if n == 0 {
|
||||||
|
vec![]
|
||||||
|
} else {
|
||||||
|
let ctors = collect_ctors(matrix);
|
||||||
|
let num_seen = ctors.len();
|
||||||
|
|
||||||
|
if num_seen == 0 {
|
||||||
|
let new_matrix: Vec<_> = matrix
|
||||||
|
.iter()
|
||||||
|
.filter_map(|row| specialize_row_by_anything(row))
|
||||||
|
.collect();
|
||||||
|
let mut rest = is_exhaustive(&new_matrix, n - 1);
|
||||||
|
|
||||||
|
for row in rest.iter_mut() {
|
||||||
|
row.push(Anything);
|
||||||
|
}
|
||||||
|
|
||||||
|
rest
|
||||||
|
} else {
|
||||||
|
let alts = ctors.iter().next().unwrap().1;
|
||||||
|
|
||||||
|
let alt_list = &alts.alternatives;
|
||||||
|
let num_alts = alt_list.len();
|
||||||
|
|
||||||
|
if num_seen < num_alts {
|
||||||
|
let new_matrix: Vec<_> = matrix
|
||||||
|
.iter()
|
||||||
|
.filter_map(|row| specialize_row_by_anything(row))
|
||||||
|
.collect();
|
||||||
|
let rest: Vec<Vec<Pattern>> = is_exhaustive(&new_matrix, n - 1);
|
||||||
|
|
||||||
|
let last: _ = alt_list
|
||||||
|
.iter()
|
||||||
|
.filter_map(|r| is_missing(alts.clone(), &ctors, r));
|
||||||
|
|
||||||
|
let mut result = Vec::new();
|
||||||
|
|
||||||
|
for last_option in last {
|
||||||
|
for mut row in rest.clone() {
|
||||||
|
row.push(last_option.clone());
|
||||||
|
|
||||||
|
result.push(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
} else {
|
||||||
|
let is_alt_exhaustive = |Ctor { arity, tag_id, .. }| {
|
||||||
|
let new_matrix: Vec<_> = matrix
|
||||||
|
.iter()
|
||||||
|
.filter_map(|r| specialize_row_by_ctor(tag_id, arity, r))
|
||||||
|
.collect();
|
||||||
|
let rest: Vec<Vec<Pattern>> = is_exhaustive(&new_matrix, arity + n - 1);
|
||||||
|
|
||||||
|
let mut result = Vec::with_capacity(rest.len());
|
||||||
|
for row in rest {
|
||||||
|
result.push(recover_ctor(alts.clone(), tag_id, arity, row));
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
};
|
||||||
|
|
||||||
|
alt_list
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.map(is_alt_exhaustive)
|
||||||
|
.flatten()
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_missing<T>(union: Union, ctors: &MutMap<TagId, T>, ctor: &Ctor) -> Option<Pattern> {
|
||||||
|
let Ctor { arity, tag_id, .. } = ctor;
|
||||||
|
|
||||||
|
if ctors.contains_key(tag_id) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let anythings = std::iter::repeat(Anything).take(*arity).collect();
|
||||||
|
Some(Pattern::Ctor(union, *tag_id, anythings))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn recover_ctor(
|
||||||
|
union: Union,
|
||||||
|
tag_id: TagId,
|
||||||
|
arity: usize,
|
||||||
|
mut patterns: Vec<Pattern>,
|
||||||
|
) -> Vec<Pattern> {
|
||||||
|
let mut rest = patterns.split_off(arity);
|
||||||
|
let args = patterns;
|
||||||
|
|
||||||
|
rest.push(Ctor(union, tag_id, args));
|
||||||
|
|
||||||
|
rest
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if a new row "vector" is useful given previous rows "matrix"
|
||||||
|
pub fn is_useful(mut old_matrix: PatternMatrix, mut vector: Row) -> bool {
|
||||||
|
let mut matrix = Vec::with_capacity(old_matrix.len());
|
||||||
|
|
||||||
|
// this loop ping-pongs the rows between old_matrix and matrix
|
||||||
|
'outer: loop {
|
||||||
|
match vector.pop() {
|
||||||
|
_ if old_matrix.is_empty() => {
|
||||||
|
// No rows are the same as the new vector! The vector is useful!
|
||||||
|
break true;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// There is nothing left in the new vector, but we still have
|
||||||
|
// rows that match the same things. This is not a useful vector!
|
||||||
|
break false;
|
||||||
|
}
|
||||||
|
Some(first_pattern) => {
|
||||||
|
// NOTE: if there are bugs in this code, look at the ordering of the row/matrix
|
||||||
|
|
||||||
|
match first_pattern {
|
||||||
|
// keep checking rows that start with this Ctor or Anything
|
||||||
|
Ctor(_, id, args) => {
|
||||||
|
specialize_row_by_ctor2(id, args.len(), &mut old_matrix, &mut matrix);
|
||||||
|
|
||||||
|
std::mem::swap(&mut old_matrix, &mut matrix);
|
||||||
|
|
||||||
|
vector.extend(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
Anything => {
|
||||||
|
// check if all alternatives appear in matrix
|
||||||
|
match is_complete(&old_matrix) {
|
||||||
|
Complete::No => {
|
||||||
|
// This Anything is useful because some Ctors are missing.
|
||||||
|
// But what if a previous row has an Anything?
|
||||||
|
// If so, this one is not useful.
|
||||||
|
for mut row in old_matrix.drain(..) {
|
||||||
|
if let Some(Anything) = row.pop() {
|
||||||
|
matrix.push(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::mem::swap(&mut old_matrix, &mut matrix);
|
||||||
|
}
|
||||||
|
Complete::Yes(alternatives) => {
|
||||||
|
// All Ctors are covered, so this Anything is not needed for any
|
||||||
|
// of those. But what if some of those Ctors have subpatterns
|
||||||
|
// that make them less general? If so, this actually is useful!
|
||||||
|
for alternative in alternatives {
|
||||||
|
let Ctor { arity, tag_id, .. } = alternative;
|
||||||
|
|
||||||
|
let mut old_matrix = old_matrix.clone();
|
||||||
|
let mut matrix = vec![];
|
||||||
|
specialize_row_by_ctor2(
|
||||||
|
tag_id,
|
||||||
|
arity,
|
||||||
|
&mut old_matrix,
|
||||||
|
&mut matrix,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut vector = vector.clone();
|
||||||
|
vector.extend(std::iter::repeat(Anything).take(arity));
|
||||||
|
|
||||||
|
if is_useful(matrix, vector) {
|
||||||
|
break 'outer true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Literal(literal) => {
|
||||||
|
// keep checking rows that start with this Literal or Anything
|
||||||
|
|
||||||
|
for mut row in old_matrix.drain(..) {
|
||||||
|
let head = row.pop();
|
||||||
|
let patterns = row;
|
||||||
|
|
||||||
|
match head {
|
||||||
|
Some(Literal(lit)) => {
|
||||||
|
if lit == literal {
|
||||||
|
matrix.push(patterns);
|
||||||
|
} else {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(Anything) => matrix.push(patterns),
|
||||||
|
|
||||||
|
Some(Ctor(_, _, _)) => panic!(
|
||||||
|
r#"Compiler bug! After type checking, constructors and literals should never align in pattern match exhaustiveness checks."#
|
||||||
|
),
|
||||||
|
|
||||||
|
None => panic!(
|
||||||
|
"Compiler error! Empty matrices should not get specialized."
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::mem::swap(&mut old_matrix, &mut matrix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// INVARIANT: (length row == N) ==> (length result == arity + N - 1)
|
||||||
|
fn specialize_row_by_ctor2(
|
||||||
|
tag_id: TagId,
|
||||||
|
arity: usize,
|
||||||
|
old_matrix: &mut PatternMatrix,
|
||||||
|
matrix: &mut PatternMatrix,
|
||||||
|
) {
|
||||||
|
for mut row in old_matrix.drain(..) {
|
||||||
|
let head = row.pop();
|
||||||
|
let mut patterns = row;
|
||||||
|
|
||||||
|
match head {
|
||||||
|
Some(Ctor(_, id, args)) =>
|
||||||
|
if id == tag_id {
|
||||||
|
patterns.extend(args);
|
||||||
|
matrix.push(patterns);
|
||||||
|
} else {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
Some(Anything) => {
|
||||||
|
// TODO order!
|
||||||
|
patterns.extend(std::iter::repeat(Anything).take(arity));
|
||||||
|
matrix.push(patterns);
|
||||||
|
}
|
||||||
|
Some(Literal(_)) => panic!( "Compiler bug! After type checking, constructors and literal should never align in pattern match exhaustiveness checks."),
|
||||||
|
None => panic!("Compiler error! Empty matrices should not get specialized."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// INVARIANT: (length row == N) ==> (length result == arity + N - 1)
|
||||||
|
fn specialize_row_by_ctor(tag_id: TagId, arity: usize, row: &RefRow) -> Option<Row> {
|
||||||
|
let mut row = row.to_vec();
|
||||||
|
|
||||||
|
let head = row.pop();
|
||||||
|
let patterns = row;
|
||||||
|
|
||||||
|
match head {
|
||||||
|
Some(Ctor(_, id, args)) => {
|
||||||
|
if id == tag_id {
|
||||||
|
// TODO order!
|
||||||
|
let mut new_patterns = Vec::new();
|
||||||
|
new_patterns.extend(args);
|
||||||
|
new_patterns.extend(patterns);
|
||||||
|
Some(new_patterns)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(Anything) => {
|
||||||
|
// TODO order!
|
||||||
|
let new_patterns = std::iter::repeat(Anything)
|
||||||
|
.take(arity)
|
||||||
|
.chain(patterns)
|
||||||
|
.collect();
|
||||||
|
Some(new_patterns)
|
||||||
|
}
|
||||||
|
Some(Literal(_)) => unreachable!(
|
||||||
|
r#"Compiler bug! After type checking, a constructor can never align with a literal: that should be a type error!"#
|
||||||
|
),
|
||||||
|
None => panic!("Compiler error! Empty matrices should not get specialized."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// INVARIANT: (length row == N) ==> (length result == N-1)
|
||||||
|
fn specialize_row_by_anything(row: &RefRow) -> Option<Row> {
|
||||||
|
let mut row = row.to_vec();
|
||||||
|
|
||||||
|
match row.pop() {
|
||||||
|
Some(Anything) => Some(row),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ALL CONSTRUCTORS ARE PRESENT?
|
||||||
|
|
||||||
|
pub enum Complete {
|
||||||
|
Yes(Vec<Ctor>),
|
||||||
|
No,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_complete(matrix: &RefPatternMatrix) -> Complete {
|
||||||
|
let ctors = collect_ctors(matrix);
|
||||||
|
let length = ctors.len();
|
||||||
|
let mut it = ctors.into_iter();
|
||||||
|
|
||||||
|
match it.next() {
|
||||||
|
None => Complete::No,
|
||||||
|
Some((_, Union { alternatives, .. })) => {
|
||||||
|
if length == alternatives.len() {
|
||||||
|
Complete::Yes(alternatives)
|
||||||
|
} else {
|
||||||
|
Complete::No
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// COLLECT CTORS
|
||||||
|
|
||||||
|
type RefPatternMatrix = [Vec<Pattern>];
|
||||||
|
type PatternMatrix = Vec<Vec<Pattern>>;
|
||||||
|
type RefRow = [Pattern];
|
||||||
|
type Row = Vec<Pattern>;
|
||||||
|
|
||||||
|
fn collect_ctors(matrix: &RefPatternMatrix) -> MutMap<TagId, Union> {
|
||||||
|
let mut ctors = MutMap::default();
|
||||||
|
|
||||||
|
for row in matrix {
|
||||||
|
if let Some(Ctor(union, id, _)) = row.get(row.len() - 1) {
|
||||||
|
ctors.insert(*id, union.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctors
|
||||||
|
}
|
|
@ -40,6 +40,8 @@ pub struct Uppercase(IdentStr);
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
|
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
|
||||||
pub struct ForeignSymbol(IdentStr);
|
pub struct ForeignSymbol(IdentStr);
|
||||||
|
|
||||||
|
pub type TagIdIntType = u16;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
pub enum TagName {
|
pub enum TagName {
|
||||||
/// Global tags have no module, but tend to be short strings (since they're
|
/// Global tags have no module, but tend to be short strings (since they're
|
||||||
|
|
|
@ -7,6 +7,7 @@ edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
roc_collections = { path = "../collections" }
|
roc_collections = { path = "../collections" }
|
||||||
|
roc_exhaustive = { path = "../exhaustive" }
|
||||||
roc_region = { path = "../region" }
|
roc_region = { path = "../region" }
|
||||||
roc_module = { path = "../module" }
|
roc_module = { path = "../module" }
|
||||||
roc_types = { path = "../types" }
|
roc_types = { path = "../types" }
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use crate::exhaustive::{Ctor, RenderAs, TagId, Union};
|
|
||||||
use crate::ir::{
|
use crate::ir::{
|
||||||
BranchInfo, DestructType, Env, Expr, JoinPointId, Literal, Param, Pattern, Procs, Stmt,
|
BranchInfo, DestructType, Env, Expr, JoinPointId, Literal, Param, Pattern, Procs, Stmt,
|
||||||
};
|
};
|
||||||
use crate::layout::{Builtin, Layout, LayoutCache, TagIdIntType, UnionLayout};
|
use crate::layout::{Builtin, Layout, LayoutCache, TagIdIntType, UnionLayout};
|
||||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||||
use roc_collections::all::{MutMap, MutSet};
|
use roc_collections::all::{MutMap, MutSet};
|
||||||
|
use roc_exhaustive::{Ctor, RenderAs, TagId, Union};
|
||||||
use roc_module::ident::TagName;
|
use roc_module::ident::TagName;
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
|
@ -83,7 +83,7 @@ enum Test<'a> {
|
||||||
IsCtor {
|
IsCtor {
|
||||||
tag_id: TagIdIntType,
|
tag_id: TagIdIntType,
|
||||||
tag_name: TagName,
|
tag_name: TagName,
|
||||||
union: crate::exhaustive::Union,
|
union: roc_exhaustive::Union,
|
||||||
arguments: Vec<(Pattern<'a>, Layout<'a>)>,
|
arguments: Vec<(Pattern<'a>, Layout<'a>)>,
|
||||||
},
|
},
|
||||||
IsInt(i128, IntWidth),
|
IsInt(i128, IntWidth),
|
||||||
|
|
|
@ -1,67 +1,12 @@
|
||||||
use crate::{ir::DestructType, layout::TagIdIntType};
|
use crate::ir::DestructType;
|
||||||
use roc_collections::all::{Index, MutMap};
|
use roc_collections::all::Index;
|
||||||
use roc_module::ident::{Lowercase, TagName};
|
use roc_exhaustive::{
|
||||||
|
is_useful, Context, Ctor, Error, Guard, Literal, Pattern, RenderAs, TagId, Union,
|
||||||
|
};
|
||||||
|
use roc_module::ident::{TagIdIntType, TagName};
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_std::RocDec;
|
|
||||||
|
|
||||||
use self::Pattern::*;
|
use Pattern::*;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub struct Union {
|
|
||||||
pub alternatives: Vec<Ctor>,
|
|
||||||
pub render_as: RenderAs,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Union {
|
|
||||||
pub fn newtype_wrapper(tag_name: TagName, arity: usize) -> Self {
|
|
||||||
let alternatives = vec![Ctor {
|
|
||||||
name: tag_name,
|
|
||||||
tag_id: TagId(0),
|
|
||||||
arity,
|
|
||||||
}];
|
|
||||||
|
|
||||||
Union {
|
|
||||||
alternatives,
|
|
||||||
render_as: RenderAs::Tag,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub enum RenderAs {
|
|
||||||
Tag,
|
|
||||||
Opaque,
|
|
||||||
Record(Vec<Lowercase>),
|
|
||||||
Guard,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Copy)]
|
|
||||||
pub struct TagId(pub TagIdIntType);
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub struct Ctor {
|
|
||||||
pub name: TagName,
|
|
||||||
pub tag_id: TagId,
|
|
||||||
pub arity: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum Pattern {
|
|
||||||
Anything,
|
|
||||||
Literal(Literal),
|
|
||||||
Ctor(Union, TagId, std::vec::Vec<Pattern>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum Literal {
|
|
||||||
Int(i128),
|
|
||||||
U128(u128),
|
|
||||||
Bit(bool),
|
|
||||||
Byte(u8),
|
|
||||||
Float(u64),
|
|
||||||
Decimal(RocDec),
|
|
||||||
Str(Box<str>),
|
|
||||||
}
|
|
||||||
|
|
||||||
fn simplify(pattern: &crate::ir::Pattern) -> Pattern {
|
fn simplify(pattern: &crate::ir::Pattern) -> Pattern {
|
||||||
use crate::ir::Pattern::*;
|
use crate::ir::Pattern::*;
|
||||||
|
@ -153,33 +98,6 @@ fn simplify(pattern: &crate::ir::Pattern) -> Pattern {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Error
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum Error {
|
|
||||||
Incomplete(Region, Context, Vec<Pattern>),
|
|
||||||
Redundant {
|
|
||||||
overall_region: Region,
|
|
||||||
branch_region: Region,
|
|
||||||
index: Index,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum Context {
|
|
||||||
BadArg,
|
|
||||||
BadDestruct,
|
|
||||||
BadCase,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum Guard {
|
|
||||||
HasGuard,
|
|
||||||
NoGuard,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check
|
|
||||||
|
|
||||||
pub fn check(
|
pub fn check(
|
||||||
region: Region,
|
region: Region,
|
||||||
patterns: &[(Loc<crate::ir::Pattern>, Guard)],
|
patterns: &[(Loc<crate::ir::Pattern>, Guard)],
|
||||||
|
@ -204,128 +122,13 @@ pub fn check_patterns<'a>(
|
||||||
match to_nonredundant_rows(region, patterns) {
|
match to_nonredundant_rows(region, patterns) {
|
||||||
Err(err) => errors.push(err),
|
Err(err) => errors.push(err),
|
||||||
Ok(matrix) => {
|
Ok(matrix) => {
|
||||||
let bad_patterns = is_exhaustive(&matrix, 1);
|
if let Err(err) = roc_exhaustive::check(region, context, matrix) {
|
||||||
if !bad_patterns.is_empty() {
|
*errors = err;
|
||||||
// TODO i suspect this is like a concat in in practice? code below can panic
|
|
||||||
// if this debug_assert! ever fails, the theory is disproven
|
|
||||||
debug_assert!(
|
|
||||||
bad_patterns.iter().map(|v| v.len()).sum::<usize>() == bad_patterns.len()
|
|
||||||
);
|
|
||||||
let heads = bad_patterns.into_iter().map(|mut v| v.remove(0)).collect();
|
|
||||||
errors.push(Error::Incomplete(region, context, heads));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// EXHAUSTIVE PATTERNS
|
|
||||||
|
|
||||||
/// INVARIANTS:
|
|
||||||
///
|
|
||||||
/// The initial rows "matrix" are all of length 1
|
|
||||||
/// The initial count of items per row "n" is also 1
|
|
||||||
/// The resulting rows are examples of missing patterns
|
|
||||||
fn is_exhaustive(matrix: &PatternMatrix, n: usize) -> PatternMatrix {
|
|
||||||
if matrix.is_empty() {
|
|
||||||
vec![std::iter::repeat(Anything).take(n).collect()]
|
|
||||||
} else if n == 0 {
|
|
||||||
vec![]
|
|
||||||
} else {
|
|
||||||
let ctors = collect_ctors(matrix);
|
|
||||||
let num_seen = ctors.len();
|
|
||||||
|
|
||||||
if num_seen == 0 {
|
|
||||||
let new_matrix = matrix
|
|
||||||
.iter()
|
|
||||||
.filter_map(specialize_row_by_anything)
|
|
||||||
.collect();
|
|
||||||
let mut rest = is_exhaustive(&new_matrix, n - 1);
|
|
||||||
|
|
||||||
for row in rest.iter_mut() {
|
|
||||||
row.push(Anything);
|
|
||||||
}
|
|
||||||
|
|
||||||
rest
|
|
||||||
} else {
|
|
||||||
let alts = ctors.iter().next().unwrap().1;
|
|
||||||
|
|
||||||
let alt_list = &alts.alternatives;
|
|
||||||
let num_alts = alt_list.len();
|
|
||||||
|
|
||||||
if num_seen < num_alts {
|
|
||||||
let new_matrix = matrix
|
|
||||||
.iter()
|
|
||||||
.filter_map(specialize_row_by_anything)
|
|
||||||
.collect();
|
|
||||||
let rest: Vec<Vec<Pattern>> = is_exhaustive(&new_matrix, n - 1);
|
|
||||||
|
|
||||||
let last: _ = alt_list
|
|
||||||
.iter()
|
|
||||||
.filter_map(|r| is_missing(alts.clone(), &ctors, r));
|
|
||||||
|
|
||||||
let mut result = Vec::new();
|
|
||||||
|
|
||||||
for last_option in last {
|
|
||||||
for mut row in rest.clone() {
|
|
||||||
row.push(last_option.clone());
|
|
||||||
|
|
||||||
result.push(row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
} else {
|
|
||||||
let is_alt_exhaustive = |Ctor { arity, tag_id, .. }| {
|
|
||||||
let new_matrix = matrix
|
|
||||||
.iter()
|
|
||||||
.filter_map(|r| specialize_row_by_ctor(tag_id, arity, r))
|
|
||||||
.collect();
|
|
||||||
let rest: Vec<Vec<Pattern>> = is_exhaustive(&new_matrix, arity + n - 1);
|
|
||||||
|
|
||||||
let mut result = Vec::with_capacity(rest.len());
|
|
||||||
for row in rest {
|
|
||||||
result.push(recover_ctor(alts.clone(), tag_id, arity, row));
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
};
|
|
||||||
|
|
||||||
alt_list
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.map(is_alt_exhaustive)
|
|
||||||
.flatten()
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_missing<T>(union: Union, ctors: &MutMap<TagId, T>, ctor: &Ctor) -> Option<Pattern> {
|
|
||||||
let Ctor { arity, tag_id, .. } = ctor;
|
|
||||||
|
|
||||||
if ctors.contains_key(tag_id) {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
let anythings = std::iter::repeat(Anything).take(*arity).collect();
|
|
||||||
Some(Pattern::Ctor(union, *tag_id, anythings))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn recover_ctor(
|
|
||||||
union: Union,
|
|
||||||
tag_id: TagId,
|
|
||||||
arity: usize,
|
|
||||||
mut patterns: Vec<Pattern>,
|
|
||||||
) -> Vec<Pattern> {
|
|
||||||
let mut rest = patterns.split_off(arity);
|
|
||||||
let args = patterns;
|
|
||||||
|
|
||||||
rest.push(Ctor(union, tag_id, args));
|
|
||||||
|
|
||||||
rest
|
|
||||||
}
|
|
||||||
|
|
||||||
/// REDUNDANT PATTERNS
|
/// REDUNDANT PATTERNS
|
||||||
|
|
||||||
/// INVARIANT: Produces a list of rows where (forall row. length row == 1)
|
/// INVARIANT: Produces a list of rows where (forall row. length row == 1)
|
||||||
|
@ -393,226 +196,3 @@ fn to_nonredundant_rows(
|
||||||
|
|
||||||
Ok(checked_rows)
|
Ok(checked_rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a new row "vector" is useful given previous rows "matrix"
|
|
||||||
fn is_useful(mut old_matrix: PatternMatrix, mut vector: Row) -> bool {
|
|
||||||
let mut matrix = Vec::with_capacity(old_matrix.len());
|
|
||||||
|
|
||||||
// this loop ping-pongs the rows between old_matrix and matrix
|
|
||||||
'outer: loop {
|
|
||||||
match vector.pop() {
|
|
||||||
_ if old_matrix.is_empty() => {
|
|
||||||
// No rows are the same as the new vector! The vector is useful!
|
|
||||||
break true;
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
// There is nothing left in the new vector, but we still have
|
|
||||||
// rows that match the same things. This is not a useful vector!
|
|
||||||
break false;
|
|
||||||
}
|
|
||||||
Some(first_pattern) => {
|
|
||||||
// NOTE: if there are bugs in this code, look at the ordering of the row/matrix
|
|
||||||
|
|
||||||
match first_pattern {
|
|
||||||
// keep checking rows that start with this Ctor or Anything
|
|
||||||
Ctor(_, id, args) => {
|
|
||||||
specialize_row_by_ctor2(id, args.len(), &mut old_matrix, &mut matrix);
|
|
||||||
|
|
||||||
std::mem::swap(&mut old_matrix, &mut matrix);
|
|
||||||
|
|
||||||
vector.extend(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
Anything => {
|
|
||||||
// check if all alternatives appear in matrix
|
|
||||||
match is_complete(&old_matrix) {
|
|
||||||
Complete::No => {
|
|
||||||
// This Anything is useful because some Ctors are missing.
|
|
||||||
// But what if a previous row has an Anything?
|
|
||||||
// If so, this one is not useful.
|
|
||||||
for mut row in old_matrix.drain(..) {
|
|
||||||
if let Some(Anything) = row.pop() {
|
|
||||||
matrix.push(row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::mem::swap(&mut old_matrix, &mut matrix);
|
|
||||||
}
|
|
||||||
Complete::Yes(alternatives) => {
|
|
||||||
// All Ctors are covered, so this Anything is not needed for any
|
|
||||||
// of those. But what if some of those Ctors have subpatterns
|
|
||||||
// that make them less general? If so, this actually is useful!
|
|
||||||
for alternative in alternatives {
|
|
||||||
let Ctor { arity, tag_id, .. } = alternative;
|
|
||||||
|
|
||||||
let mut old_matrix = old_matrix.clone();
|
|
||||||
let mut matrix = vec![];
|
|
||||||
specialize_row_by_ctor2(
|
|
||||||
tag_id,
|
|
||||||
arity,
|
|
||||||
&mut old_matrix,
|
|
||||||
&mut matrix,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut vector = vector.clone();
|
|
||||||
vector.extend(std::iter::repeat(Anything).take(arity));
|
|
||||||
|
|
||||||
if is_useful(matrix, vector) {
|
|
||||||
break 'outer true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Literal(literal) => {
|
|
||||||
// keep checking rows that start with this Literal or Anything
|
|
||||||
|
|
||||||
for mut row in old_matrix.drain(..) {
|
|
||||||
let head = row.pop();
|
|
||||||
let patterns = row;
|
|
||||||
|
|
||||||
match head {
|
|
||||||
Some(Literal(lit)) => {
|
|
||||||
if lit == literal {
|
|
||||||
matrix.push(patterns);
|
|
||||||
} else {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(Anything) => matrix.push(patterns),
|
|
||||||
|
|
||||||
Some(Ctor(_, _, _)) => panic!(
|
|
||||||
r#"Compiler bug! After type checking, constructors and literals should never align in pattern match exhaustiveness checks."#
|
|
||||||
),
|
|
||||||
|
|
||||||
None => panic!(
|
|
||||||
"Compiler error! Empty matrices should not get specialized."
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::mem::swap(&mut old_matrix, &mut matrix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// INVARIANT: (length row == N) ==> (length result == arity + N - 1)
|
|
||||||
fn specialize_row_by_ctor2(
|
|
||||||
tag_id: TagId,
|
|
||||||
arity: usize,
|
|
||||||
old_matrix: &mut PatternMatrix,
|
|
||||||
matrix: &mut PatternMatrix,
|
|
||||||
) {
|
|
||||||
for mut row in old_matrix.drain(..) {
|
|
||||||
let head = row.pop();
|
|
||||||
let mut patterns = row;
|
|
||||||
|
|
||||||
match head {
|
|
||||||
Some(Ctor(_, id, args)) =>
|
|
||||||
if id == tag_id {
|
|
||||||
patterns.extend(args);
|
|
||||||
matrix.push(patterns);
|
|
||||||
} else {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
Some(Anything) => {
|
|
||||||
// TODO order!
|
|
||||||
patterns.extend(std::iter::repeat(Anything).take(arity));
|
|
||||||
matrix.push(patterns);
|
|
||||||
}
|
|
||||||
Some(Literal(_)) => panic!( "Compiler bug! After type checking, constructors and literal should never align in pattern match exhaustiveness checks."),
|
|
||||||
None => panic!("Compiler error! Empty matrices should not get specialized."),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// INVARIANT: (length row == N) ==> (length result == arity + N - 1)
|
|
||||||
fn specialize_row_by_ctor(tag_id: TagId, arity: usize, row: &Row) -> Option<Row> {
|
|
||||||
let mut row = row.clone();
|
|
||||||
|
|
||||||
let head = row.pop();
|
|
||||||
let patterns = row;
|
|
||||||
|
|
||||||
match head {
|
|
||||||
Some(Ctor(_, id, args)) => {
|
|
||||||
if id == tag_id {
|
|
||||||
// TODO order!
|
|
||||||
let mut new_patterns = Vec::new();
|
|
||||||
new_patterns.extend(args);
|
|
||||||
new_patterns.extend(patterns);
|
|
||||||
Some(new_patterns)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(Anything) => {
|
|
||||||
// TODO order!
|
|
||||||
let new_patterns = std::iter::repeat(Anything)
|
|
||||||
.take(arity)
|
|
||||||
.chain(patterns)
|
|
||||||
.collect();
|
|
||||||
Some(new_patterns)
|
|
||||||
}
|
|
||||||
Some(Literal(_)) => unreachable!(
|
|
||||||
r#"Compiler bug! After type checking, a constructor can never align with a literal: that should be a type error!"#
|
|
||||||
),
|
|
||||||
None => panic!("Compiler error! Empty matrices should not get specialized."),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// INVARIANT: (length row == N) ==> (length result == N-1)
|
|
||||||
fn specialize_row_by_anything(row: &Row) -> Option<Row> {
|
|
||||||
let mut row = row.clone();
|
|
||||||
|
|
||||||
match row.pop() {
|
|
||||||
Some(Anything) => Some(row),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ALL CONSTRUCTORS ARE PRESENT?
|
|
||||||
|
|
||||||
pub enum Complete {
|
|
||||||
Yes(Vec<Ctor>),
|
|
||||||
No,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_complete(matrix: &PatternMatrix) -> Complete {
|
|
||||||
let ctors = collect_ctors(matrix);
|
|
||||||
let length = ctors.len();
|
|
||||||
let mut it = ctors.into_iter();
|
|
||||||
|
|
||||||
match it.next() {
|
|
||||||
None => Complete::No,
|
|
||||||
Some((_, Union { alternatives, .. })) => {
|
|
||||||
if length == alternatives.len() {
|
|
||||||
Complete::Yes(alternatives)
|
|
||||||
} else {
|
|
||||||
Complete::No
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// COLLECT CTORS
|
|
||||||
|
|
||||||
type RefPatternMatrix = [Vec<Pattern>];
|
|
||||||
type PatternMatrix = Vec<Vec<Pattern>>;
|
|
||||||
type Row = Vec<Pattern>;
|
|
||||||
|
|
||||||
fn collect_ctors(matrix: &RefPatternMatrix) -> MutMap<TagId, Union> {
|
|
||||||
let mut ctors = MutMap::default();
|
|
||||||
|
|
||||||
for row in matrix {
|
|
||||||
if let Some(Ctor(union, id, _)) = row.get(row.len() - 1) {
|
|
||||||
ctors.insert(*id, union.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctors
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#![allow(clippy::manual_map)]
|
#![allow(clippy::manual_map)]
|
||||||
|
|
||||||
use crate::exhaustive::{Ctor, Guard, RenderAs, TagId};
|
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Builtin, ClosureRepresentation, LambdaSet, Layout, LayoutCache, LayoutProblem,
|
Builtin, ClosureRepresentation, LambdaSet, Layout, LayoutCache, LayoutProblem,
|
||||||
RawFunctionLayout, TagIdIntType, UnionLayout, WrappedVariant,
|
RawFunctionLayout, TagIdIntType, UnionLayout, WrappedVariant,
|
||||||
|
@ -10,6 +9,7 @@ use bumpalo::Bump;
|
||||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||||
use roc_can::expr::{ClosureData, IntValue};
|
use roc_can::expr::{ClosureData, IntValue};
|
||||||
use roc_collections::all::{default_hasher, BumpMap, BumpMapDefault, MutMap};
|
use roc_collections::all::{default_hasher, BumpMap, BumpMapDefault, MutMap};
|
||||||
|
use roc_exhaustive::{Ctor, Guard, RenderAs, TagId};
|
||||||
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
||||||
|
@ -95,7 +95,7 @@ pub enum OptLevel {
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum MonoProblem {
|
pub enum MonoProblem {
|
||||||
PatternProblem(crate::exhaustive::Error),
|
PatternProblem(roc_exhaustive::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
@ -1895,7 +1895,7 @@ fn patterns_to_when<'a>(
|
||||||
// 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, pattern) in patterns.into_iter() {
|
||||||
let context = crate::exhaustive::Context::BadArg;
|
let context = roc_exhaustive::Context::BadArg;
|
||||||
let mono_pattern = match from_can_pattern(env, layout_cache, &pattern.value) {
|
let mono_pattern = match from_can_pattern(env, layout_cache, &pattern.value) {
|
||||||
Ok((pat, _assignments)) => {
|
Ok((pat, _assignments)) => {
|
||||||
// Don't apply any assignments (e.g. to initialize optional variables) yet.
|
// Don't apply any assignments (e.g. to initialize optional variables) yet.
|
||||||
|
@ -1921,7 +1921,7 @@ fn patterns_to_when<'a>(
|
||||||
pattern.region,
|
pattern.region,
|
||||||
&[(
|
&[(
|
||||||
Loc::at(pattern.region, mono_pattern),
|
Loc::at(pattern.region, mono_pattern),
|
||||||
crate::exhaustive::Guard::NoGuard,
|
roc_exhaustive::Guard::NoGuard,
|
||||||
)],
|
)],
|
||||||
context,
|
context,
|
||||||
) {
|
) {
|
||||||
|
@ -3279,12 +3279,12 @@ pub fn with_hole<'a>(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let context = crate::exhaustive::Context::BadDestruct;
|
let context = roc_exhaustive::Context::BadDestruct;
|
||||||
match crate::exhaustive::check(
|
match crate::exhaustive::check(
|
||||||
def.loc_pattern.region,
|
def.loc_pattern.region,
|
||||||
&[(
|
&[(
|
||||||
Loc::at(def.loc_pattern.region, mono_pattern.clone()),
|
Loc::at(def.loc_pattern.region, mono_pattern.clone()),
|
||||||
crate::exhaustive::Guard::NoGuard,
|
roc_exhaustive::Guard::NoGuard,
|
||||||
)],
|
)],
|
||||||
context,
|
context,
|
||||||
) {
|
) {
|
||||||
|
@ -5619,12 +5619,12 @@ pub fn from_can<'a>(
|
||||||
hole,
|
hole,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let context = crate::exhaustive::Context::BadDestruct;
|
let context = roc_exhaustive::Context::BadDestruct;
|
||||||
match crate::exhaustive::check(
|
match crate::exhaustive::check(
|
||||||
def.loc_pattern.region,
|
def.loc_pattern.region,
|
||||||
&[(
|
&[(
|
||||||
Loc::at(def.loc_pattern.region, mono_pattern.clone()),
|
Loc::at(def.loc_pattern.region, mono_pattern.clone()),
|
||||||
crate::exhaustive::Guard::NoGuard,
|
roc_exhaustive::Guard::NoGuard,
|
||||||
)],
|
)],
|
||||||
context,
|
context,
|
||||||
) {
|
) {
|
||||||
|
@ -5750,11 +5750,11 @@ fn to_opt_branches<'a>(
|
||||||
// NOTE exhaustiveness is checked after the construction of all the branches
|
// NOTE exhaustiveness is checked after the construction of all the branches
|
||||||
// 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 = crate::exhaustive::Context::BadCase;
|
let context = roc_exhaustive::Context::BadCase;
|
||||||
match crate::exhaustive::check(region, &loc_branches, context) {
|
match crate::exhaustive::check(region, &loc_branches, context) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(errors) => {
|
Err(errors) => {
|
||||||
use crate::exhaustive::Error::*;
|
use roc_exhaustive::Error::*;
|
||||||
let mut is_not_exhaustive = false;
|
let mut is_not_exhaustive = false;
|
||||||
let mut overlapping_branches = std::vec::Vec::new();
|
let mut overlapping_branches = std::vec::Vec::new();
|
||||||
|
|
||||||
|
@ -7657,12 +7657,12 @@ pub enum Pattern<'a> {
|
||||||
BitLiteral {
|
BitLiteral {
|
||||||
value: bool,
|
value: bool,
|
||||||
tag_name: TagName,
|
tag_name: TagName,
|
||||||
union: crate::exhaustive::Union,
|
union: roc_exhaustive::Union,
|
||||||
},
|
},
|
||||||
EnumLiteral {
|
EnumLiteral {
|
||||||
tag_id: u8,
|
tag_id: u8,
|
||||||
tag_name: TagName,
|
tag_name: TagName,
|
||||||
union: crate::exhaustive::Union,
|
union: roc_exhaustive::Union,
|
||||||
},
|
},
|
||||||
StrLiteral(Box<str>),
|
StrLiteral(Box<str>),
|
||||||
|
|
||||||
|
@ -7676,7 +7676,7 @@ pub enum Pattern<'a> {
|
||||||
tag_id: TagIdIntType,
|
tag_id: TagIdIntType,
|
||||||
arguments: Vec<'a, (Pattern<'a>, Layout<'a>)>,
|
arguments: Vec<'a, (Pattern<'a>, Layout<'a>)>,
|
||||||
layout: UnionLayout<'a>,
|
layout: UnionLayout<'a>,
|
||||||
union: crate::exhaustive::Union,
|
union: roc_exhaustive::Union,
|
||||||
},
|
},
|
||||||
OpaqueUnwrap {
|
OpaqueUnwrap {
|
||||||
opaque: Symbol,
|
opaque: Symbol,
|
||||||
|
@ -7817,8 +7817,8 @@ fn from_can_pattern_help<'a>(
|
||||||
arguments,
|
arguments,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
use crate::exhaustive::Union;
|
|
||||||
use crate::layout::UnionVariant::*;
|
use crate::layout::UnionVariant::*;
|
||||||
|
use roc_exhaustive::Union;
|
||||||
|
|
||||||
let res_variant =
|
let res_variant =
|
||||||
crate::layout::union_sorted_tags(env.arena, *whole_var, env.subs, env.target_info)
|
crate::layout::union_sorted_tags(env.arena, *whole_var, env.subs, env.target_info)
|
||||||
|
@ -7883,7 +7883,7 @@ fn from_can_pattern_help<'a>(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let union = crate::exhaustive::Union {
|
let union = roc_exhaustive::Union {
|
||||||
render_as: RenderAs::Tag,
|
render_as: RenderAs::Tag,
|
||||||
alternatives: ctors,
|
alternatives: ctors,
|
||||||
};
|
};
|
||||||
|
@ -7974,7 +7974,7 @@ fn from_can_pattern_help<'a>(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let union = crate::exhaustive::Union {
|
let union = roc_exhaustive::Union {
|
||||||
render_as: RenderAs::Tag,
|
render_as: RenderAs::Tag,
|
||||||
alternatives: ctors,
|
alternatives: ctors,
|
||||||
};
|
};
|
||||||
|
@ -8026,7 +8026,7 @@ fn from_can_pattern_help<'a>(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let union = crate::exhaustive::Union {
|
let union = roc_exhaustive::Union {
|
||||||
render_as: RenderAs::Tag,
|
render_as: RenderAs::Tag,
|
||||||
alternatives: ctors,
|
alternatives: ctors,
|
||||||
};
|
};
|
||||||
|
@ -8069,7 +8069,7 @@ fn from_can_pattern_help<'a>(
|
||||||
arity: fields.len(),
|
arity: fields.len(),
|
||||||
});
|
});
|
||||||
|
|
||||||
let union = crate::exhaustive::Union {
|
let union = roc_exhaustive::Union {
|
||||||
render_as: RenderAs::Tag,
|
render_as: RenderAs::Tag,
|
||||||
alternatives: ctors,
|
alternatives: ctors,
|
||||||
};
|
};
|
||||||
|
@ -8139,7 +8139,7 @@ fn from_can_pattern_help<'a>(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let union = crate::exhaustive::Union {
|
let union = roc_exhaustive::Union {
|
||||||
render_as: RenderAs::Tag,
|
render_as: RenderAs::Tag,
|
||||||
alternatives: ctors,
|
alternatives: ctors,
|
||||||
};
|
};
|
||||||
|
@ -8194,7 +8194,7 @@ fn from_can_pattern_help<'a>(
|
||||||
arity: other_fields.len() - 1,
|
arity: other_fields.len() - 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
let union = crate::exhaustive::Union {
|
let union = roc_exhaustive::Union {
|
||||||
render_as: RenderAs::Tag,
|
render_as: RenderAs::Tag,
|
||||||
alternatives: ctors,
|
alternatives: ctors,
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,6 +7,7 @@ edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
roc_collections = { path = "../compiler/collections" }
|
roc_collections = { path = "../compiler/collections" }
|
||||||
|
roc_exhaustive = { path = "../compiler/exhaustive" }
|
||||||
roc_region = { path = "../compiler/region" }
|
roc_region = { path = "../compiler/region" }
|
||||||
roc_module = { path = "../compiler/module" }
|
roc_module = { path = "../compiler/module" }
|
||||||
roc_parse = { path = "../compiler/parse" }
|
roc_parse = { path = "../compiler/parse" }
|
||||||
|
|
|
@ -10,8 +10,8 @@ pub fn mono_problem<'b>(
|
||||||
filename: PathBuf,
|
filename: PathBuf,
|
||||||
problem: roc_mono::ir::MonoProblem,
|
problem: roc_mono::ir::MonoProblem,
|
||||||
) -> Report<'b> {
|
) -> Report<'b> {
|
||||||
use roc_mono::exhaustive::Context::*;
|
use roc_exhaustive::Context::*;
|
||||||
use roc_mono::exhaustive::Error::*;
|
use roc_exhaustive::Error::*;
|
||||||
use roc_mono::ir::MonoProblem::*;
|
use roc_mono::ir::MonoProblem::*;
|
||||||
|
|
||||||
match problem {
|
match problem {
|
||||||
|
@ -121,7 +121,7 @@ pub fn mono_problem<'b>(
|
||||||
|
|
||||||
pub fn unhandled_patterns_to_doc_block<'b>(
|
pub fn unhandled_patterns_to_doc_block<'b>(
|
||||||
alloc: &'b RocDocAllocator<'b>,
|
alloc: &'b RocDocAllocator<'b>,
|
||||||
patterns: Vec<roc_mono::exhaustive::Pattern>,
|
patterns: Vec<roc_exhaustive::Pattern>,
|
||||||
) -> RocDocBuilder<'b> {
|
) -> RocDocBuilder<'b> {
|
||||||
alloc
|
alloc
|
||||||
.vcat(patterns.into_iter().map(|v| pattern_to_doc(alloc, v)))
|
.vcat(patterns.into_iter().map(|v| pattern_to_doc(alloc, v)))
|
||||||
|
@ -131,19 +131,19 @@ pub fn unhandled_patterns_to_doc_block<'b>(
|
||||||
|
|
||||||
fn pattern_to_doc<'b>(
|
fn pattern_to_doc<'b>(
|
||||||
alloc: &'b RocDocAllocator<'b>,
|
alloc: &'b RocDocAllocator<'b>,
|
||||||
pattern: roc_mono::exhaustive::Pattern,
|
pattern: roc_exhaustive::Pattern,
|
||||||
) -> RocDocBuilder<'b> {
|
) -> RocDocBuilder<'b> {
|
||||||
pattern_to_doc_help(alloc, pattern, false)
|
pattern_to_doc_help(alloc, pattern, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pattern_to_doc_help<'b>(
|
fn pattern_to_doc_help<'b>(
|
||||||
alloc: &'b RocDocAllocator<'b>,
|
alloc: &'b RocDocAllocator<'b>,
|
||||||
pattern: roc_mono::exhaustive::Pattern,
|
pattern: roc_exhaustive::Pattern,
|
||||||
in_type_param: bool,
|
in_type_param: bool,
|
||||||
) -> RocDocBuilder<'b> {
|
) -> RocDocBuilder<'b> {
|
||||||
use roc_mono::exhaustive::Literal::*;
|
use roc_exhaustive::Literal::*;
|
||||||
use roc_mono::exhaustive::Pattern::*;
|
use roc_exhaustive::Pattern::*;
|
||||||
use roc_mono::exhaustive::RenderAs;
|
use roc_exhaustive::RenderAs;
|
||||||
|
|
||||||
match pattern {
|
match pattern {
|
||||||
Anything => alloc.text("_"),
|
Anything => alloc.text("_"),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue