mirror of
https://github.com/roc-lang/roc.git
synced 2024-11-11 05:34:11 +03:00
remove now-unused mono files
This commit is contained in:
parent
378fe1d5d1
commit
9dbce40a55
@ -1,6 +1,6 @@
|
|||||||
use crate::expr::{DestructType, Env, Expr, Pattern};
|
use crate::exhaustive::{Ctor, RenderAs, TagId, Union};
|
||||||
|
use crate::ir::{DestructType, Env, Expr, JoinPointId, Literal, Pattern, Stmt};
|
||||||
use crate::layout::{Builtin, Layout};
|
use crate::layout::{Builtin, Layout};
|
||||||
use crate::pattern::{Ctor, RenderAs, TagId, Union};
|
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_collections::all::{MutMap, MutSet};
|
use roc_collections::all::{MutMap, MutSet};
|
||||||
use roc_module::ident::TagName;
|
use roc_module::ident::TagName;
|
||||||
@ -31,8 +31,12 @@ pub fn compile<'a>(raw_branches: Vec<(Guard<'a>, Pattern<'a>, u64)>) -> Decision
|
|||||||
pub enum Guard<'a> {
|
pub enum Guard<'a> {
|
||||||
NoGuard,
|
NoGuard,
|
||||||
Guard {
|
Guard {
|
||||||
stores: &'a [(Symbol, Layout<'a>, Expr<'a>)],
|
/// Symbol that stores a boolean
|
||||||
expr: Expr<'a>,
|
/// when true this branch is picked, otherwise skipped
|
||||||
|
symbol: Symbol,
|
||||||
|
/// after assigning to symbol, the stmt jumps to this label
|
||||||
|
id: JoinPointId,
|
||||||
|
stmt: Stmt<'a>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +61,7 @@ pub enum Test<'a> {
|
|||||||
IsCtor {
|
IsCtor {
|
||||||
tag_id: u8,
|
tag_id: u8,
|
||||||
tag_name: TagName,
|
tag_name: TagName,
|
||||||
union: crate::pattern::Union,
|
union: crate::exhaustive::Union,
|
||||||
arguments: Vec<(Pattern<'a>, Layout<'a>)>,
|
arguments: Vec<(Pattern<'a>, Layout<'a>)>,
|
||||||
},
|
},
|
||||||
IsInt(i64),
|
IsInt(i64),
|
||||||
@ -72,8 +76,12 @@ pub enum Test<'a> {
|
|||||||
// A pattern that always succeeds (like `_`) can still have a guard
|
// A pattern that always succeeds (like `_`) can still have a guard
|
||||||
Guarded {
|
Guarded {
|
||||||
opt_test: Option<Box<Test<'a>>>,
|
opt_test: Option<Box<Test<'a>>>,
|
||||||
stores: &'a [(Symbol, Layout<'a>, Expr<'a>)],
|
/// Symbol that stores a boolean
|
||||||
expr: Expr<'a>,
|
/// when true this branch is picked, otherwise skipped
|
||||||
|
symbol: Symbol,
|
||||||
|
/// after assigning to symbol, the stmt jumps to this label
|
||||||
|
id: JoinPointId,
|
||||||
|
stmt: Stmt<'a>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
@ -353,11 +361,12 @@ fn test_at_path<'a>(selected_path: &Path, branch: Branch<'a>, all_tests: &mut Ve
|
|||||||
None => {}
|
None => {}
|
||||||
Some((_, guard, pattern)) => {
|
Some((_, guard, pattern)) => {
|
||||||
let guarded = |test| {
|
let guarded = |test| {
|
||||||
if let Guard::Guard { stores, expr } = guard {
|
if let Guard::Guard { symbol, id, stmt } = guard {
|
||||||
Guarded {
|
Guarded {
|
||||||
opt_test: Some(Box::new(test)),
|
opt_test: Some(Box::new(test)),
|
||||||
stores,
|
stmt: stmt.clone(),
|
||||||
expr: expr.clone(),
|
symbol: *symbol,
|
||||||
|
id: *id,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
test
|
test
|
||||||
@ -367,11 +376,12 @@ fn test_at_path<'a>(selected_path: &Path, branch: Branch<'a>, all_tests: &mut Ve
|
|||||||
match pattern {
|
match pattern {
|
||||||
// TODO use guard!
|
// TODO use guard!
|
||||||
Identifier(_) | Underscore | Shadowed(_, _) | UnsupportedPattern(_) => {
|
Identifier(_) | Underscore | Shadowed(_, _) | UnsupportedPattern(_) => {
|
||||||
if let Guard::Guard { stores, expr } = guard {
|
if let Guard::Guard { symbol, id, stmt } = guard {
|
||||||
all_tests.push(Guarded {
|
all_tests.push(Guarded {
|
||||||
opt_test: None,
|
opt_test: None,
|
||||||
stores,
|
stmt: stmt.clone(),
|
||||||
expr: expr.clone(),
|
symbol: *symbol,
|
||||||
|
id: *id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -575,6 +585,8 @@ fn to_relevant_branch_help<'a>(
|
|||||||
start.push((Path::Unbox(Box::new(path.clone())), guard, arg.0));
|
start.push((Path::Unbox(Box::new(path.clone())), guard, arg.0));
|
||||||
start.extend(end);
|
start.extend(end);
|
||||||
}
|
}
|
||||||
|
} else if union.alternatives.len() == 1 {
|
||||||
|
todo!("this should need a special index, right?")
|
||||||
} else {
|
} else {
|
||||||
let sub_positions =
|
let sub_positions =
|
||||||
arguments
|
arguments
|
||||||
@ -863,31 +875,29 @@ enum Decider<'a, T> {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
enum Choice<'a> {
|
enum Choice<'a> {
|
||||||
Inline(Stores<'a>, Expr<'a>),
|
Inline(Stmt<'a>),
|
||||||
Jump(Label),
|
Jump(Label),
|
||||||
}
|
}
|
||||||
|
|
||||||
type Stores<'a> = &'a [(Symbol, Layout<'a>, Expr<'a>)];
|
type Stores<'a> = &'a [(Symbol, Layout<'a>, Expr<'a>)];
|
||||||
|
type StoresVec<'a> = bumpalo::collections::Vec<'a, (Symbol, Layout<'a>, Expr<'a>)>;
|
||||||
|
|
||||||
pub fn optimize_when<'a>(
|
pub fn optimize_when<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
cond_symbol: Symbol,
|
cond_symbol: Symbol,
|
||||||
cond_layout: Layout<'a>,
|
cond_layout: Layout<'a>,
|
||||||
ret_layout: Layout<'a>,
|
ret_layout: Layout<'a>,
|
||||||
opt_branches: Vec<(Pattern<'a>, Guard<'a>, Stores<'a>, Expr<'a>)>,
|
opt_branches: bumpalo::collections::Vec<'a, (Pattern<'a>, Guard<'a>, Stmt<'a>)>,
|
||||||
) -> Expr<'a> {
|
) -> Stmt<'a> {
|
||||||
let (patterns, _indexed_branches) = opt_branches
|
let (patterns, _indexed_branches) = opt_branches
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(index, (pattern, guard, stores, branch))| {
|
.map(|(index, (pattern, guard, branch))| {
|
||||||
(
|
((guard, pattern, index as u64), (index as u64, branch))
|
||||||
(guard, pattern, index as u64),
|
|
||||||
(index as u64, stores, branch),
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
.unzip();
|
.unzip();
|
||||||
|
|
||||||
let indexed_branches: Vec<(u64, Stores<'a>, Expr<'a>)> = _indexed_branches;
|
let indexed_branches: Vec<(u64, Stmt<'a>)> = _indexed_branches;
|
||||||
|
|
||||||
let decision_tree = compile(patterns);
|
let decision_tree = compile(patterns);
|
||||||
let decider = tree_to_decider(decision_tree);
|
let decider = tree_to_decider(decision_tree);
|
||||||
@ -896,9 +906,8 @@ pub fn optimize_when<'a>(
|
|||||||
let mut choices = MutMap::default();
|
let mut choices = MutMap::default();
|
||||||
let mut jumps = Vec::new();
|
let mut jumps = Vec::new();
|
||||||
|
|
||||||
for (index, stores, branch) in indexed_branches.into_iter() {
|
for (index, branch) in indexed_branches.into_iter() {
|
||||||
let ((branch_index, choice), opt_jump) =
|
let ((branch_index, choice), opt_jump) = create_choices(&target_counts, index, branch);
|
||||||
create_choices(&target_counts, index, stores, branch);
|
|
||||||
|
|
||||||
if let Some(jump) = opt_jump {
|
if let Some(jump) = opt_jump {
|
||||||
jumps.push(jump);
|
jumps.push(jump);
|
||||||
@ -909,7 +918,7 @@ pub fn optimize_when<'a>(
|
|||||||
|
|
||||||
let choice_decider = insert_choices(&choices, decider);
|
let choice_decider = insert_choices(&choices, decider);
|
||||||
|
|
||||||
let (stores, expr) = decide_to_branching(
|
let expr = decide_to_branching(
|
||||||
env,
|
env,
|
||||||
cond_symbol,
|
cond_symbol,
|
||||||
cond_layout,
|
cond_layout,
|
||||||
@ -921,7 +930,7 @@ pub fn optimize_when<'a>(
|
|||||||
// increase the jump counter by the number of jumps in this branching structure
|
// increase the jump counter by the number of jumps in this branching structure
|
||||||
*env.jump_counter += jumps.len() as u64;
|
*env.jump_counter += jumps.len() as u64;
|
||||||
|
|
||||||
Expr::Store(stores, env.arena.alloc(expr))
|
expr
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_to_expr<'a>(
|
fn path_to_expr<'a>(
|
||||||
@ -929,47 +938,62 @@ fn path_to_expr<'a>(
|
|||||||
symbol: Symbol,
|
symbol: Symbol,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
layout: &Layout<'a>,
|
layout: &Layout<'a>,
|
||||||
) -> Expr<'a> {
|
) -> (StoresVec<'a>, Symbol) {
|
||||||
path_to_expr_help(env, symbol, path, layout.clone()).0
|
let (symbol, stores, _) = path_to_expr_help2(env, symbol, path, layout.clone());
|
||||||
|
|
||||||
|
(stores, symbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_to_expr_help<'a>(
|
fn path_to_expr_help2<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
symbol: Symbol,
|
mut symbol: Symbol,
|
||||||
path: &Path,
|
mut path: &Path,
|
||||||
layout: Layout<'a>,
|
mut layout: Layout<'a>,
|
||||||
) -> (Expr<'a>, Layout<'a>) {
|
) -> (Symbol, StoresVec<'a>, Layout<'a>) {
|
||||||
match path {
|
let mut stores = bumpalo::collections::Vec::new_in(env.arena);
|
||||||
Path::Unbox(unboxed) => path_to_expr_help(env, symbol, unboxed, layout),
|
|
||||||
Path::Empty => (Expr::Load(symbol), layout),
|
|
||||||
|
|
||||||
Path::Index {
|
loop {
|
||||||
index,
|
match path {
|
||||||
tag_id,
|
Path::Unbox(unboxed) => {
|
||||||
path: nested,
|
path = unboxed;
|
||||||
} => {
|
}
|
||||||
let (outer_expr, outer_layout) = path_to_expr_help(env, symbol, nested, layout);
|
Path::Empty => break,
|
||||||
|
|
||||||
let (is_unwrapped, field_layouts) = match outer_layout {
|
Path::Index {
|
||||||
Layout::Union(layouts) => (layouts.is_empty(), layouts[*tag_id as usize].to_vec()),
|
index,
|
||||||
Layout::Struct(layouts) => (true, layouts.to_vec()),
|
tag_id,
|
||||||
other => (true, vec![other]),
|
path: nested,
|
||||||
};
|
} => {
|
||||||
|
let (is_unwrapped, field_layouts) = match layout.clone() {
|
||||||
|
Layout::Union(layouts) => {
|
||||||
|
(layouts.is_empty(), layouts[*tag_id as usize].to_vec())
|
||||||
|
}
|
||||||
|
Layout::Struct(layouts) => (true, layouts.to_vec()),
|
||||||
|
other => (true, vec![other]),
|
||||||
|
};
|
||||||
|
|
||||||
debug_assert!(*index < field_layouts.len() as u64);
|
debug_assert!(*index < field_layouts.len() as u64);
|
||||||
|
|
||||||
let inner_layout = field_layouts[*index as usize].clone();
|
let inner_layout = field_layouts[*index as usize].clone();
|
||||||
|
|
||||||
let inner_expr = Expr::AccessAtIndex {
|
let inner_expr = Expr::AccessAtIndex {
|
||||||
index: *index,
|
index: *index,
|
||||||
field_layouts: env.arena.alloc(field_layouts),
|
field_layouts: env.arena.alloc(field_layouts),
|
||||||
expr: env.arena.alloc(outer_expr),
|
//structure: env.arena.alloc(outer_expr),
|
||||||
is_unwrapped,
|
structure: symbol,
|
||||||
};
|
is_unwrapped,
|
||||||
|
};
|
||||||
|
|
||||||
(inner_expr, inner_layout)
|
symbol = env.unique_symbol();
|
||||||
|
stores.push((symbol, inner_layout.clone(), inner_expr));
|
||||||
|
|
||||||
|
layout = inner_layout;
|
||||||
|
path = nested;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(symbol, stores, layout)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_to_equality<'a>(
|
fn test_to_equality<'a>(
|
||||||
@ -978,8 +1002,7 @@ fn test_to_equality<'a>(
|
|||||||
cond_layout: &Layout<'a>,
|
cond_layout: &Layout<'a>,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
test: Test<'a>,
|
test: Test<'a>,
|
||||||
tests: &mut Vec<(Expr<'a>, Expr<'a>, Layout<'a>)>,
|
) -> (StoresVec<'a>, Symbol, Symbol, Layout<'a>) {
|
||||||
) {
|
|
||||||
match test {
|
match test {
|
||||||
Test::IsCtor {
|
Test::IsCtor {
|
||||||
tag_id,
|
tag_id,
|
||||||
@ -991,7 +1014,7 @@ fn test_to_equality<'a>(
|
|||||||
// (e.g. record pattern guard matches)
|
// (e.g. record pattern guard matches)
|
||||||
debug_assert!(union.alternatives.len() > 1);
|
debug_assert!(union.alternatives.len() > 1);
|
||||||
|
|
||||||
let lhs = Expr::Int(tag_id as i64);
|
let lhs = Expr::Literal(Literal::Int(tag_id as i64));
|
||||||
|
|
||||||
let mut field_layouts =
|
let mut field_layouts =
|
||||||
bumpalo::collections::Vec::with_capacity_in(arguments.len(), env.arena);
|
bumpalo::collections::Vec::with_capacity_in(arguments.len(), env.arena);
|
||||||
@ -1006,65 +1029,101 @@ fn test_to_equality<'a>(
|
|||||||
let rhs = Expr::AccessAtIndex {
|
let rhs = Expr::AccessAtIndex {
|
||||||
index: 0,
|
index: 0,
|
||||||
field_layouts: field_layouts.into_bump_slice(),
|
field_layouts: field_layouts.into_bump_slice(),
|
||||||
expr: env.arena.alloc(Expr::Load(cond_symbol)),
|
structure: cond_symbol,
|
||||||
is_unwrapped: union.alternatives.len() == 1,
|
is_unwrapped: union.alternatives.len() == 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
tests.push((lhs, rhs, Layout::Builtin(Builtin::Int64)));
|
let lhs_symbol = env.unique_symbol();
|
||||||
|
let rhs_symbol = env.unique_symbol();
|
||||||
|
|
||||||
|
let mut stores = bumpalo::collections::Vec::with_capacity_in(2, env.arena);
|
||||||
|
|
||||||
|
stores.push((lhs_symbol, Layout::Builtin(Builtin::Int64), lhs));
|
||||||
|
stores.push((rhs_symbol, Layout::Builtin(Builtin::Int64), rhs));
|
||||||
|
|
||||||
|
(
|
||||||
|
stores,
|
||||||
|
lhs_symbol,
|
||||||
|
rhs_symbol,
|
||||||
|
Layout::Builtin(Builtin::Int64),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Test::IsInt(test_int) => {
|
Test::IsInt(test_int) => {
|
||||||
let lhs = Expr::Int(test_int);
|
let lhs = Expr::Literal(Literal::Int(test_int));
|
||||||
let rhs = path_to_expr(env, cond_symbol, &path, &cond_layout);
|
let lhs_symbol = env.unique_symbol();
|
||||||
|
let (mut stores, rhs_symbol) = path_to_expr(env, cond_symbol, &path, &cond_layout);
|
||||||
|
stores.push((lhs_symbol, Layout::Builtin(Builtin::Int64), lhs));
|
||||||
|
|
||||||
tests.push((lhs, rhs, Layout::Builtin(Builtin::Int64)));
|
(
|
||||||
|
stores,
|
||||||
|
lhs_symbol,
|
||||||
|
rhs_symbol,
|
||||||
|
Layout::Builtin(Builtin::Int64),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Test::IsFloat(test_int) => {
|
Test::IsFloat(test_int) => {
|
||||||
// TODO maybe we can actually use i64 comparison here?
|
// TODO maybe we can actually use i64 comparison here?
|
||||||
let test_float = f64::from_bits(test_int as u64);
|
let test_float = f64::from_bits(test_int as u64);
|
||||||
let lhs = Expr::Float(test_float);
|
let lhs = Expr::Literal(Literal::Float(test_float));
|
||||||
let rhs = path_to_expr(env, cond_symbol, &path, &cond_layout);
|
let lhs_symbol = env.unique_symbol();
|
||||||
|
let (mut stores, rhs_symbol) = path_to_expr(env, cond_symbol, &path, &cond_layout);
|
||||||
|
stores.push((lhs_symbol, Layout::Builtin(Builtin::Float64), lhs));
|
||||||
|
|
||||||
tests.push((lhs, rhs, Layout::Builtin(Builtin::Float64)));
|
(
|
||||||
|
stores,
|
||||||
|
lhs_symbol,
|
||||||
|
rhs_symbol,
|
||||||
|
Layout::Builtin(Builtin::Float64),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Test::IsByte {
|
Test::IsByte {
|
||||||
tag_id: test_byte, ..
|
tag_id: test_byte, ..
|
||||||
} => {
|
} => {
|
||||||
let lhs = Expr::Byte(test_byte);
|
let lhs = Expr::Literal(Literal::Byte(test_byte));
|
||||||
let rhs = path_to_expr(env, cond_symbol, &path, &cond_layout);
|
let lhs_symbol = env.unique_symbol();
|
||||||
|
let (mut stores, rhs_symbol) = path_to_expr(env, cond_symbol, &path, &cond_layout);
|
||||||
|
stores.push((lhs_symbol, Layout::Builtin(Builtin::Int8), lhs));
|
||||||
|
|
||||||
tests.push((lhs, rhs, Layout::Builtin(Builtin::Int8)));
|
(
|
||||||
|
stores,
|
||||||
|
lhs_symbol,
|
||||||
|
rhs_symbol,
|
||||||
|
Layout::Builtin(Builtin::Int8),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Test::IsBit(test_bit) => {
|
Test::IsBit(test_bit) => {
|
||||||
let lhs = Expr::Bool(test_bit);
|
let lhs = Expr::Literal(Literal::Bool(test_bit));
|
||||||
let rhs = path_to_expr(env, cond_symbol, &path, &cond_layout);
|
let lhs_symbol = env.unique_symbol();
|
||||||
|
let (mut stores, rhs_symbol) = path_to_expr(env, cond_symbol, &path, &cond_layout);
|
||||||
|
stores.push((lhs_symbol, Layout::Builtin(Builtin::Int1), lhs));
|
||||||
|
|
||||||
tests.push((lhs, rhs, Layout::Builtin(Builtin::Int1)));
|
(
|
||||||
|
stores,
|
||||||
|
lhs_symbol,
|
||||||
|
rhs_symbol,
|
||||||
|
Layout::Builtin(Builtin::Int1),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Test::IsStr(test_str) => {
|
Test::IsStr(test_str) => {
|
||||||
let lhs = Expr::Str(env.arena.alloc(test_str));
|
let lhs = Expr::Literal(Literal::Str(env.arena.alloc(test_str)));
|
||||||
let rhs = path_to_expr(env, cond_symbol, &path, &cond_layout);
|
let lhs_symbol = env.unique_symbol();
|
||||||
|
let (mut stores, rhs_symbol) = path_to_expr(env, cond_symbol, &path, &cond_layout);
|
||||||
|
|
||||||
tests.push((lhs, rhs, Layout::Builtin(Builtin::Str)));
|
stores.push((lhs_symbol, Layout::Builtin(Builtin::Str), lhs));
|
||||||
|
|
||||||
|
(
|
||||||
|
stores,
|
||||||
|
lhs_symbol,
|
||||||
|
rhs_symbol,
|
||||||
|
Layout::Builtin(Builtin::Str),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Test::Guarded {
|
Test::Guarded { .. } => unreachable!("should be handled elsewhere"),
|
||||||
opt_test,
|
|
||||||
stores,
|
|
||||||
expr,
|
|
||||||
} => {
|
|
||||||
if let Some(nested) = opt_test {
|
|
||||||
test_to_equality(env, cond_symbol, cond_layout, path, *nested, tests);
|
|
||||||
}
|
|
||||||
|
|
||||||
let lhs = Expr::Bool(true);
|
|
||||||
let rhs = Expr::Store(stores, env.arena.alloc(expr));
|
|
||||||
|
|
||||||
tests.push((lhs, rhs, Layout::Builtin(Builtin::Int1)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1074,21 +1133,21 @@ fn decide_to_branching<'a>(
|
|||||||
cond_layout: Layout<'a>,
|
cond_layout: Layout<'a>,
|
||||||
ret_layout: Layout<'a>,
|
ret_layout: Layout<'a>,
|
||||||
decider: Decider<'a, Choice<'a>>,
|
decider: Decider<'a, Choice<'a>>,
|
||||||
jumps: &Vec<(u64, Stores<'a>, Expr<'a>)>,
|
jumps: &Vec<(u64, Stmt<'a>)>,
|
||||||
) -> (Stores<'a>, Expr<'a>) {
|
) -> Stmt<'a> {
|
||||||
use Choice::*;
|
use Choice::*;
|
||||||
use Decider::*;
|
use Decider::*;
|
||||||
|
|
||||||
match decider {
|
match decider {
|
||||||
Leaf(Jump(label)) => {
|
Leaf(Jump(label)) => {
|
||||||
// we currently inline the jumps: does fewer jumps but produces a larger artifact
|
// we currently inline the jumps: does fewer jumps but produces a larger artifact
|
||||||
let (_, stores, expr) = jumps
|
let (_, expr) = jumps
|
||||||
.iter()
|
.iter()
|
||||||
.find(|(l, _, _)| l == &label)
|
.find(|(l, _)| l == &label)
|
||||||
.expect("jump not in list of jumps");
|
.expect("jump not in list of jumps");
|
||||||
(stores, expr.clone())
|
expr.clone()
|
||||||
}
|
}
|
||||||
Leaf(Inline(stores, expr)) => (stores, expr),
|
Leaf(Inline(expr)) => expr,
|
||||||
Chain {
|
Chain {
|
||||||
test_chain,
|
test_chain,
|
||||||
success,
|
success,
|
||||||
@ -1096,13 +1155,7 @@ fn decide_to_branching<'a>(
|
|||||||
} => {
|
} => {
|
||||||
// generate a switch based on the test chain
|
// generate a switch based on the test chain
|
||||||
|
|
||||||
let mut tests = Vec::with_capacity(test_chain.len());
|
let pass_expr = decide_to_branching(
|
||||||
|
|
||||||
for (path, test) in test_chain {
|
|
||||||
test_to_equality(env, cond_symbol, &cond_layout, &path, test, &mut tests);
|
|
||||||
}
|
|
||||||
|
|
||||||
let (pass_stores, pass_expr) = decide_to_branching(
|
|
||||||
env,
|
env,
|
||||||
cond_symbol,
|
cond_symbol,
|
||||||
cond_layout.clone(),
|
cond_layout.clone(),
|
||||||
@ -1111,7 +1164,7 @@ fn decide_to_branching<'a>(
|
|||||||
jumps,
|
jumps,
|
||||||
);
|
);
|
||||||
|
|
||||||
let (fail_stores, fail_expr) = decide_to_branching(
|
let fail_expr = decide_to_branching(
|
||||||
env,
|
env,
|
||||||
cond_symbol,
|
cond_symbol,
|
||||||
cond_layout.clone(),
|
cond_layout.clone(),
|
||||||
@ -1120,31 +1173,148 @@ fn decide_to_branching<'a>(
|
|||||||
jumps,
|
jumps,
|
||||||
);
|
);
|
||||||
|
|
||||||
let fail = (fail_stores, &*env.arena.alloc(fail_expr));
|
let fail = &*env.arena.alloc(fail_expr);
|
||||||
let pass = (pass_stores, &*env.arena.alloc(pass_expr));
|
let pass = &*env.arena.alloc(pass_expr);
|
||||||
|
|
||||||
let condition = boolean_all(env.arena, tests);
|
// TODO totally wrong
|
||||||
|
let condition = Expr::Literal(Literal::Int(42));
|
||||||
|
|
||||||
let branching_symbol = env.unique_symbol();
|
let branching_symbol = env.unique_symbol();
|
||||||
let stores = [(branching_symbol, Layout::Builtin(Builtin::Int1), condition)];
|
|
||||||
|
|
||||||
let branching_layout = Layout::Builtin(Builtin::Int1);
|
let branching_layout = Layout::Builtin(Builtin::Int1);
|
||||||
|
|
||||||
(
|
let mut cond = Stmt::Cond {
|
||||||
env.arena.alloc(stores),
|
cond_symbol,
|
||||||
Expr::Store(
|
cond_layout: cond_layout.clone(),
|
||||||
&[],
|
branching_symbol,
|
||||||
env.arena.alloc(Expr::Cond {
|
branching_layout,
|
||||||
|
pass,
|
||||||
|
fail,
|
||||||
|
ret_layout,
|
||||||
|
};
|
||||||
|
|
||||||
|
let true_symbol = env.unique_symbol();
|
||||||
|
|
||||||
|
let mut tests = Vec::with_capacity(test_chain.len());
|
||||||
|
|
||||||
|
let mut guard = None;
|
||||||
|
|
||||||
|
// Assumption: there is at most 1 guard, and it is the outer layer.
|
||||||
|
for (path, test) in test_chain {
|
||||||
|
match test {
|
||||||
|
Test::Guarded {
|
||||||
|
opt_test,
|
||||||
|
id,
|
||||||
|
symbol,
|
||||||
|
stmt,
|
||||||
|
} => {
|
||||||
|
if let Some(nested) = opt_test {
|
||||||
|
tests.push(test_to_equality(
|
||||||
|
env,
|
||||||
|
cond_symbol,
|
||||||
|
&cond_layout,
|
||||||
|
&path,
|
||||||
|
*nested,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// let (stores, rhs_symbol) = path_to_expr(env, cond_symbol, &path, &cond_layout);
|
||||||
|
|
||||||
|
guard = Some((symbol, id, stmt));
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => tests.push(test_to_equality(
|
||||||
|
env,
|
||||||
cond_symbol,
|
cond_symbol,
|
||||||
cond_layout,
|
&cond_layout,
|
||||||
branching_symbol,
|
&path,
|
||||||
branching_layout,
|
test,
|
||||||
pass,
|
)),
|
||||||
fail,
|
}
|
||||||
ret_layout,
|
}
|
||||||
}),
|
|
||||||
),
|
let mut current_symbol = branching_symbol;
|
||||||
)
|
|
||||||
|
// TODO There must be some way to remove this iterator/loop
|
||||||
|
let nr = (tests.len() as i64) - 1 + (guard.is_some() as i64);
|
||||||
|
let accum_symbols = std::iter::once(true_symbol)
|
||||||
|
.chain((0..nr).map(|_| env.unique_symbol()))
|
||||||
|
.rev()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let mut accum_it = accum_symbols.into_iter();
|
||||||
|
|
||||||
|
// the guard is the final thing that we check, so needs to be layered on first!
|
||||||
|
if let Some((_, id, stmt)) = guard {
|
||||||
|
let accum = accum_it.next().unwrap();
|
||||||
|
let test_symbol = env.unique_symbol();
|
||||||
|
|
||||||
|
let and_expr =
|
||||||
|
Expr::RunLowLevel(LowLevel::And, env.arena.alloc([test_symbol, accum]));
|
||||||
|
|
||||||
|
// write to the branching symbol
|
||||||
|
cond = Stmt::Let(
|
||||||
|
current_symbol,
|
||||||
|
and_expr,
|
||||||
|
Layout::Builtin(Builtin::Int1),
|
||||||
|
env.arena.alloc(cond),
|
||||||
|
);
|
||||||
|
|
||||||
|
// calculate the guard value
|
||||||
|
cond = Stmt::Join {
|
||||||
|
id,
|
||||||
|
arguments: env
|
||||||
|
.arena
|
||||||
|
.alloc([(test_symbol, Layout::Builtin(Builtin::Int1))]),
|
||||||
|
remainder: env.arena.alloc(stmt),
|
||||||
|
continuation: env.arena.alloc(cond),
|
||||||
|
};
|
||||||
|
|
||||||
|
current_symbol = accum;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ((new_stores, lhs, rhs, layout), accum) in tests.into_iter().rev().zip(accum_it) {
|
||||||
|
let test_symbol = env.unique_symbol();
|
||||||
|
let test = Expr::RunLowLevel(
|
||||||
|
LowLevel::Eq,
|
||||||
|
bumpalo::vec![in env.arena; lhs, rhs].into_bump_slice(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let and_expr =
|
||||||
|
Expr::RunLowLevel(LowLevel::And, env.arena.alloc([test_symbol, accum]));
|
||||||
|
|
||||||
|
// write to the branching symbol
|
||||||
|
cond = Stmt::Let(
|
||||||
|
current_symbol,
|
||||||
|
and_expr,
|
||||||
|
Layout::Builtin(Builtin::Int1),
|
||||||
|
env.arena.alloc(cond),
|
||||||
|
);
|
||||||
|
|
||||||
|
// write to the test symbol
|
||||||
|
cond = Stmt::Let(
|
||||||
|
test_symbol,
|
||||||
|
test,
|
||||||
|
Layout::Builtin(Builtin::Int1),
|
||||||
|
env.arena.alloc(cond),
|
||||||
|
);
|
||||||
|
|
||||||
|
for (symbol, layout, expr) in new_stores.into_iter() {
|
||||||
|
cond = Stmt::Let(symbol, expr, layout, env.arena.alloc(cond));
|
||||||
|
}
|
||||||
|
|
||||||
|
current_symbol = accum;
|
||||||
|
}
|
||||||
|
|
||||||
|
cond = Stmt::Let(
|
||||||
|
true_symbol,
|
||||||
|
Expr::Literal(Literal::Bool(true)),
|
||||||
|
Layout::Builtin(Builtin::Int1),
|
||||||
|
env.arena.alloc(cond),
|
||||||
|
);
|
||||||
|
|
||||||
|
// (env.arena.alloc(stores), cond)
|
||||||
|
cond
|
||||||
}
|
}
|
||||||
FanOut {
|
FanOut {
|
||||||
path,
|
path,
|
||||||
@ -1153,9 +1323,11 @@ fn decide_to_branching<'a>(
|
|||||||
} => {
|
} => {
|
||||||
// the cond_layout can change in the process. E.g. if the cond is a Tag, we actually
|
// the cond_layout can change in the process. E.g. if the cond is a Tag, we actually
|
||||||
// switch on the tag discriminant (currently an i64 value)
|
// switch on the tag discriminant (currently an i64 value)
|
||||||
let (cond, cond_layout) = path_to_expr_help(env, cond_symbol, &path, cond_layout);
|
// NOTE the tag discriminant is not actually loaded, `cond` can point to a tag
|
||||||
|
let (cond, cond_stores_vec, cond_layout) =
|
||||||
|
path_to_expr_help2(env, cond_symbol, &path, cond_layout);
|
||||||
|
|
||||||
let (default_stores, default_expr) = decide_to_branching(
|
let default_branch = decide_to_branching(
|
||||||
env,
|
env,
|
||||||
cond_symbol,
|
cond_symbol,
|
||||||
cond_layout.clone(),
|
cond_layout.clone(),
|
||||||
@ -1163,12 +1335,11 @@ fn decide_to_branching<'a>(
|
|||||||
*fallback,
|
*fallback,
|
||||||
jumps,
|
jumps,
|
||||||
);
|
);
|
||||||
let default_branch = (default_stores, &*env.arena.alloc(default_expr));
|
|
||||||
|
|
||||||
let mut branches = bumpalo::collections::Vec::with_capacity_in(tests.len(), env.arena);
|
let mut branches = bumpalo::collections::Vec::with_capacity_in(tests.len(), env.arena);
|
||||||
|
|
||||||
for (test, decider) in tests {
|
for (test, decider) in tests {
|
||||||
let (stores, branch) = decide_to_branching(
|
let branch = decide_to_branching(
|
||||||
env,
|
env,
|
||||||
cond_symbol,
|
cond_symbol,
|
||||||
cond_layout.clone(),
|
cond_layout.clone(),
|
||||||
@ -1186,25 +1357,28 @@ fn decide_to_branching<'a>(
|
|||||||
other => todo!("other {:?}", other),
|
other => todo!("other {:?}", other),
|
||||||
};
|
};
|
||||||
|
|
||||||
branches.push((tag, stores, branch));
|
branches.push((tag, branch));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut switch = Stmt::Switch {
|
||||||
|
cond_layout,
|
||||||
|
cond_symbol: cond,
|
||||||
|
branches: branches.into_bump_slice(),
|
||||||
|
default_branch: env.arena.alloc(default_branch),
|
||||||
|
ret_layout,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (symbol, layout, expr) in cond_stores_vec.into_iter() {
|
||||||
|
switch = Stmt::Let(symbol, expr, layout, env.arena.alloc(switch));
|
||||||
}
|
}
|
||||||
|
|
||||||
// make a jump table based on the tests
|
// make a jump table based on the tests
|
||||||
(
|
switch
|
||||||
&[],
|
|
||||||
Expr::Switch {
|
|
||||||
cond: env.arena.alloc(cond),
|
|
||||||
cond_layout,
|
|
||||||
cond_symbol,
|
|
||||||
branches: branches.into_bump_slice(),
|
|
||||||
default_branch,
|
|
||||||
ret_layout,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
fn boolean_all<'a>(arena: &'a Bump, tests: Vec<(Expr<'a>, Expr<'a>, Layout<'a>)>) -> Expr<'a> {
|
fn boolean_all<'a>(arena: &'a Bump, tests: Vec<(Expr<'a>, Expr<'a>, Layout<'a>)>) -> Expr<'a> {
|
||||||
let mut expr = Expr::Bool(true);
|
let mut expr = Expr::Bool(true);
|
||||||
|
|
||||||
@ -1225,6 +1399,7 @@ fn boolean_all<'a>(arena: &'a Bump, tests: Vec<(Expr<'a>, Expr<'a>, Layout<'a>)>
|
|||||||
|
|
||||||
expr
|
expr
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
/// TREE TO DECIDER
|
/// TREE TO DECIDER
|
||||||
///
|
///
|
||||||
@ -1398,19 +1573,15 @@ fn count_targets_help(decision_tree: &Decider<u64>, targets: &mut MutMap<u64, u6
|
|||||||
fn create_choices<'a>(
|
fn create_choices<'a>(
|
||||||
target_counts: &MutMap<u64, u64>,
|
target_counts: &MutMap<u64, u64>,
|
||||||
target: u64,
|
target: u64,
|
||||||
stores: Stores<'a>,
|
branch: Stmt<'a>,
|
||||||
branch: Expr<'a>,
|
) -> ((u64, Choice<'a>), Option<(u64, Stmt<'a>)>) {
|
||||||
) -> ((u64, Choice<'a>), Option<(u64, Stores<'a>, Expr<'a>)>) {
|
|
||||||
match target_counts.get(&target) {
|
match target_counts.get(&target) {
|
||||||
None => unreachable!(
|
None => unreachable!(
|
||||||
"this should never happen: {:?} not in {:?}",
|
"this should never happen: {:?} not in {:?}",
|
||||||
target, target_counts
|
target, target_counts
|
||||||
),
|
),
|
||||||
Some(1) => ((target, Choice::Inline(stores, branch)), None),
|
Some(1) => ((target, Choice::Inline(branch)), None),
|
||||||
Some(_) => (
|
Some(_) => ((target, Choice::Jump(target)), Some((target, branch))),
|
||||||
(target, Choice::Jump(target)),
|
|
||||||
Some((target, stores, branch)),
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1971,7 +1971,7 @@ fn from_can_when<'a>(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::decision_tree2::Guard;
|
use crate::decision_tree::Guard;
|
||||||
match res_stores {
|
match res_stores {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
for (symbol, layout, expr) in stores.iter().rev() {
|
for (symbol, layout, expr) in stores.iter().rev() {
|
||||||
@ -2008,7 +2008,7 @@ fn from_can_when<'a>(
|
|||||||
});
|
});
|
||||||
let mono_branches = Vec::from_iter_in(it, arena);
|
let mono_branches = Vec::from_iter_in(it, arena);
|
||||||
|
|
||||||
crate::decision_tree2::optimize_when(
|
crate::decision_tree::optimize_when(
|
||||||
env,
|
env,
|
||||||
cond_symbol,
|
cond_symbol,
|
||||||
cond_layout.clone(),
|
cond_layout.clone(),
|
||||||
|
@ -19,6 +19,6 @@ pub mod layout;
|
|||||||
//#[allow(clippy::ptr_arg)]
|
//#[allow(clippy::ptr_arg)]
|
||||||
//pub mod decision_tree;
|
//pub mod decision_tree;
|
||||||
#[allow(clippy::ptr_arg)]
|
#[allow(clippy::ptr_arg)]
|
||||||
pub mod decision_tree2;
|
pub mod decision_tree;
|
||||||
#[allow(clippy::ptr_arg)]
|
#[allow(clippy::ptr_arg)]
|
||||||
pub mod exhaustive;
|
pub mod exhaustive;
|
||||||
|
@ -1,535 +0,0 @@
|
|||||||
use crate::expr::DestructType;
|
|
||||||
use roc_collections::all::{Index, MutMap};
|
|
||||||
use roc_module::ident::{Lowercase, TagName};
|
|
||||||
use roc_region::all::{Located, Region};
|
|
||||||
|
|
||||||
use self::Pattern::*;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub struct Union {
|
|
||||||
pub alternatives: Vec<Ctor>,
|
|
||||||
pub render_as: RenderAs,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub enum RenderAs {
|
|
||||||
Tag,
|
|
||||||
Record(Vec<Lowercase>),
|
|
||||||
Guard,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Copy)]
|
|
||||||
pub struct TagId(pub u8);
|
|
||||||
|
|
||||||
#[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(i64),
|
|
||||||
Bit(bool),
|
|
||||||
Byte(u8),
|
|
||||||
Float(u64),
|
|
||||||
Str(Box<str>),
|
|
||||||
}
|
|
||||||
|
|
||||||
fn simplify<'a>(pattern: &crate::expr::Pattern<'a>) -> Pattern {
|
|
||||||
use crate::expr::Pattern::*;
|
|
||||||
|
|
||||||
match pattern {
|
|
||||||
IntLiteral(v) => Literal(Literal::Int(*v)),
|
|
||||||
FloatLiteral(v) => Literal(Literal::Float(*v)),
|
|
||||||
StrLiteral(v) => Literal(Literal::Str(v.clone())),
|
|
||||||
|
|
||||||
// To make sure these are exhaustive, we have to "fake" a union here
|
|
||||||
// TODO: use the hash or some other integer to discriminate between constructors
|
|
||||||
BitLiteral { value, union, .. } => Ctor(union.clone(), TagId(*value as u8), vec![]),
|
|
||||||
EnumLiteral { tag_id, union, .. } => Ctor(union.clone(), TagId(*tag_id), vec![]),
|
|
||||||
|
|
||||||
Underscore => Anything,
|
|
||||||
Identifier(_) => Anything,
|
|
||||||
RecordDestructure(destructures, _) => {
|
|
||||||
let tag_id = TagId(0);
|
|
||||||
let mut patterns = std::vec::Vec::with_capacity(destructures.len());
|
|
||||||
let mut field_names = std::vec::Vec::with_capacity(destructures.len());
|
|
||||||
|
|
||||||
for destruct in destructures {
|
|
||||||
field_names.push(destruct.label.clone());
|
|
||||||
|
|
||||||
match &destruct.typ {
|
|
||||||
DestructType::Required | DestructType::Optional(_) => patterns.push(Anything),
|
|
||||||
DestructType::Guard(guard) => patterns.push(simplify(guard)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let union = Union {
|
|
||||||
render_as: RenderAs::Record(field_names),
|
|
||||||
alternatives: vec![Ctor {
|
|
||||||
name: TagName::Global("#Record".into()),
|
|
||||||
tag_id,
|
|
||||||
arity: destructures.len(),
|
|
||||||
}],
|
|
||||||
};
|
|
||||||
|
|
||||||
Ctor(union, tag_id, patterns)
|
|
||||||
}
|
|
||||||
|
|
||||||
Shadowed(_region, _ident) => {
|
|
||||||
// Treat as an Anything
|
|
||||||
// code-gen will make a runtime error out of the branch
|
|
||||||
Anything
|
|
||||||
}
|
|
||||||
UnsupportedPattern(_region) => {
|
|
||||||
// Treat as an Anything
|
|
||||||
// code-gen will make a runtime error out of the branch
|
|
||||||
Anything
|
|
||||||
}
|
|
||||||
|
|
||||||
AppliedTag {
|
|
||||||
tag_id,
|
|
||||||
arguments,
|
|
||||||
union,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
let simplified_args: std::vec::Vec<_> =
|
|
||||||
arguments.iter().map(|v| simplify(&v.0)).collect();
|
|
||||||
Ctor(union.clone(), TagId(*tag_id), simplified_args)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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<'a>(
|
|
||||||
region: Region,
|
|
||||||
patterns: &[(Located<crate::expr::Pattern<'a>>, Guard)],
|
|
||||||
context: Context,
|
|
||||||
) -> Result<(), Vec<Error>> {
|
|
||||||
let mut errors = Vec::new();
|
|
||||||
check_patterns(region, context, patterns, &mut errors);
|
|
||||||
|
|
||||||
if errors.is_empty() {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(errors)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_patterns<'a>(
|
|
||||||
region: Region,
|
|
||||||
context: Context,
|
|
||||||
patterns: &[(Located<crate::expr::Pattern<'a>>, Guard)],
|
|
||||||
errors: &mut Vec<Error>,
|
|
||||||
) {
|
|
||||||
match to_nonredundant_rows(region, patterns) {
|
|
||||||
Err(err) => errors.push(err),
|
|
||||||
Ok(matrix) => {
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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.clone(), 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
|
|
||||||
|
|
||||||
/// INVARIANT: Produces a list of rows where (forall row. length row == 1)
|
|
||||||
fn to_nonredundant_rows<'a>(
|
|
||||||
overall_region: Region,
|
|
||||||
patterns: &[(Located<crate::expr::Pattern<'a>>, Guard)],
|
|
||||||
) -> Result<Vec<Vec<Pattern>>, Error> {
|
|
||||||
let mut checked_rows = Vec::with_capacity(patterns.len());
|
|
||||||
|
|
||||||
// If any of the branches has a guard, e.g.
|
|
||||||
//
|
|
||||||
// when x is
|
|
||||||
// y if y < 10 -> "foo"
|
|
||||||
// _ -> "bar"
|
|
||||||
//
|
|
||||||
// then we treat it as a pattern match on the pattern and a boolean, wrapped in the #Guard
|
|
||||||
// constructor. We can use this special constructor name to generate better error messages.
|
|
||||||
// This transformation of the pattern match only works because we only report exhaustiveness
|
|
||||||
// errors: the Pattern created in this file is not used for code gen.
|
|
||||||
//
|
|
||||||
// when x is
|
|
||||||
// #Guard y True -> "foo"
|
|
||||||
// #Guard _ _ -> "bar"
|
|
||||||
let any_has_guard = patterns.iter().any(|(_, guard)| guard == &Guard::HasGuard);
|
|
||||||
|
|
||||||
for (loc_pat, guard) in patterns {
|
|
||||||
let region = loc_pat.region;
|
|
||||||
|
|
||||||
let next_row = if any_has_guard {
|
|
||||||
let guard_pattern = match guard {
|
|
||||||
Guard::HasGuard => Pattern::Literal(Literal::Bit(true)),
|
|
||||||
Guard::NoGuard => Pattern::Anything,
|
|
||||||
};
|
|
||||||
|
|
||||||
let tag_id = TagId(0);
|
|
||||||
|
|
||||||
let union = Union {
|
|
||||||
render_as: RenderAs::Guard,
|
|
||||||
alternatives: vec![Ctor {
|
|
||||||
tag_id,
|
|
||||||
name: TagName::Global("#Guard".into()),
|
|
||||||
arity: 2,
|
|
||||||
}],
|
|
||||||
};
|
|
||||||
|
|
||||||
vec![Pattern::Ctor(
|
|
||||||
union,
|
|
||||||
tag_id,
|
|
||||||
vec![simplify(&loc_pat.value), guard_pattern],
|
|
||||||
)]
|
|
||||||
} else {
|
|
||||||
vec![simplify(&loc_pat.value)]
|
|
||||||
};
|
|
||||||
|
|
||||||
if is_useful(&checked_rows, &next_row) {
|
|
||||||
checked_rows.push(next_row);
|
|
||||||
} else {
|
|
||||||
return Err(Error::Redundant {
|
|
||||||
overall_region,
|
|
||||||
branch_region: region,
|
|
||||||
index: Index::zero_based(checked_rows.len()),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(checked_rows)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check if a new row "vector" is useful given previous rows "matrix"
|
|
||||||
fn is_useful(matrix: &PatternMatrix, vector: &Row) -> bool {
|
|
||||||
if matrix.is_empty() {
|
|
||||||
// No rows are the same as the new vector! The vector is useful!
|
|
||||||
true
|
|
||||||
} else if vector.is_empty() {
|
|
||||||
// There is nothing left in the new vector, but we still have
|
|
||||||
// rows that match the same things. This is not a useful vector!
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
// NOTE: if there are bugs in this code, look at the ordering of the row/matrix
|
|
||||||
let mut vector = vector.clone();
|
|
||||||
let first_pattern = vector.remove(0);
|
|
||||||
let patterns = vector;
|
|
||||||
|
|
||||||
match first_pattern {
|
|
||||||
// keep checking rows that start with this Ctor or Anything
|
|
||||||
Ctor(_, id, args) => {
|
|
||||||
let new_matrix: Vec<_> = matrix
|
|
||||||
.iter()
|
|
||||||
.filter_map(|r| specialize_row_by_ctor(id, args.len(), r))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let mut new_row = Vec::new();
|
|
||||||
new_row.extend(patterns);
|
|
||||||
new_row.extend(args);
|
|
||||||
|
|
||||||
is_useful(&new_matrix, &new_row)
|
|
||||||
}
|
|
||||||
|
|
||||||
Anything => {
|
|
||||||
// check if all alts appear in matrix
|
|
||||||
match is_complete(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.
|
|
||||||
let new_matrix: Vec<_> = matrix
|
|
||||||
.iter()
|
|
||||||
.filter_map(|r| specialize_row_by_anything(r))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
is_useful(&new_matrix, &patterns)
|
|
||||||
}
|
|
||||||
Complete::Yes(alts) => {
|
|
||||||
// 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!
|
|
||||||
let is_useful_alt = |Ctor { arity, tag_id, .. }| {
|
|
||||||
let new_matrix = matrix
|
|
||||||
.iter()
|
|
||||||
.filter_map(|r| specialize_row_by_ctor(tag_id, arity, r))
|
|
||||||
.collect();
|
|
||||||
let mut new_row: Vec<Pattern> =
|
|
||||||
std::iter::repeat(Anything).take(arity).collect::<Vec<_>>();
|
|
||||||
|
|
||||||
new_row.extend(patterns.clone());
|
|
||||||
|
|
||||||
is_useful(&new_matrix, &new_row)
|
|
||||||
};
|
|
||||||
|
|
||||||
alts.iter().cloned().any(is_useful_alt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Literal(literal) => {
|
|
||||||
// keep checking rows that start with this Literal or Anything
|
|
||||||
let new_matrix = matrix
|
|
||||||
.iter()
|
|
||||||
.filter_map(|r| specialize_row_by_literal(&literal, r))
|
|
||||||
.collect();
|
|
||||||
is_useful(&new_matrix, &patterns)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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(_)) => 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 == N-1)
|
|
||||||
fn specialize_row_by_literal(literal: &Literal, row: &Row) -> Option<Row> {
|
|
||||||
let mut row = row.clone();
|
|
||||||
|
|
||||||
let head = row.pop();
|
|
||||||
let patterns = row;
|
|
||||||
|
|
||||||
match head {
|
|
||||||
Some(Literal(lit)) => {
|
|
||||||
if &lit == literal {
|
|
||||||
Some(patterns)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(Anything) => Some(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."),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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 mut it = ctors.values();
|
|
||||||
|
|
||||||
match it.next() {
|
|
||||||
None => Complete::No,
|
|
||||||
Some(Union { alternatives, .. }) => {
|
|
||||||
if ctors.len() == alternatives.len() {
|
|
||||||
Complete::Yes(alternatives.to_vec())
|
|
||||||
} 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
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user