type inference for pattern guards

This commit is contained in:
Folkert 2020-03-21 00:39:52 +01:00
parent 655dc32098
commit 920928399a
9 changed files with 230 additions and 57 deletions

View file

@ -30,6 +30,19 @@ pub struct Output {
pub aliases: SendMap<Symbol, Alias>,
}
impl Output {
pub fn union(&mut self, other: Self) {
self.references.union_mut(other.references);
if let (None, Some(later)) = (self.tail_call, other.tail_call) {
self.tail_call = Some(later);
}
self.introduced_variables.union(&other.introduced_variables);
self.aliases.extend(other.aliases);
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum Expr {
// Literals
@ -55,7 +68,7 @@ pub enum Expr {
cond_var: Variable,
expr_var: Variable,
loc_cond: Box<Located<Expr>>,
branches: Vec<(Located<Pattern>, Located<Expr>)>,
branches: Vec<WhenBranch>,
},
If {
cond_var: Variable,
@ -145,6 +158,13 @@ pub enum Recursive {
NotRecursive,
}
#[derive(Clone, Debug, PartialEq)]
pub struct WhenBranch {
pub patterns: Vec<Located<Pattern>>,
pub value: Located<Expr>,
pub guard: Option<Located<Expr>>,
}
pub fn canonicalize_expr<'a>(
env: &mut Env<'a>,
var_store: &VarStore,
@ -453,19 +473,12 @@ pub fn canonicalize_expr<'a>(
let mut can_branches = Vec::with_capacity(branches.len());
for branch in branches {
let (can_when_pattern, loc_can_expr, branch_references) = canonicalize_when_branch(
env,
var_store,
scope,
region,
branch.patterns.first().unwrap(),
&branch.value,
&mut output,
);
let (can_when_branch, branch_references) =
canonicalize_when_branch(env, var_store, scope, region, *branch, &mut output);
output.references = output.references.union(branch_references);
can_branches.push((can_when_pattern, loc_can_expr));
can_branches.push(can_when_branch);
}
// A "when" with no branches is a runtime error, but it will mess things up
@ -654,6 +667,77 @@ pub fn canonicalize_expr<'a>(
}
#[inline(always)]
fn canonicalize_when_branch<'a>(
env: &mut Env<'a>,
var_store: &VarStore,
scope: &mut Scope,
region: Region,
branch: &'a ast::WhenBranch<'a>,
output: &mut Output,
) -> (WhenBranch, References) {
let mut patterns = Vec::with_capacity(branch.patterns.len());
let original_scope = scope;
let mut scope = original_scope.clone();
// TODO report symbols not bound in all patterns
for loc_pattern in &branch.patterns {
patterns.push(canonicalize_pattern(
env,
var_store,
&mut scope,
WhenBranch,
&loc_pattern.value,
loc_pattern.region,
));
}
let (value, mut branch_output) = canonicalize_expr(
env,
var_store,
&mut scope,
branch.value.region,
&branch.value.value,
);
let guard = match &branch.guard {
None => None,
Some(loc_expr) => {
let (can_guard, guard_branch_output) =
canonicalize_expr(env, var_store, &mut scope, region, &loc_expr.value);
branch_output.union(guard_branch_output);
Some(can_guard)
}
};
// Now that we've collected all the references for this branch, check to see if
// any of the new idents it defined were unused. If any were, report it.
for (symbol, region) in scope.symbols() {
let symbol = *symbol;
if !output.references.has_lookup(symbol)
&& !branch_output.references.has_lookup(symbol)
&& !original_scope.contains_symbol(symbol)
{
env.problem(Problem::UnusedDef(symbol, *region));
}
}
let references = branch_output.references.clone();
output.union(branch_output);
(
WhenBranch {
patterns,
value,
guard,
},
references,
)
}
/*
fn canonicalize_when_branch<'a>(
env: &mut Env<'a>,
var_store: &VarStore,
@ -702,6 +786,7 @@ fn canonicalize_when_branch<'a>(
(loc_can_pattern, can_expr, branch_output.references)
}
*/
pub fn local_successors<'a>(
references: &'a References,