make bound variables available in guards

This commit is contained in:
Folkert 2020-03-22 01:11:19 +01:00
parent 1f3b8f7d68
commit 9a0234e5dd
3 changed files with 140 additions and 42 deletions

View file

@ -1040,13 +1040,7 @@ fn from_can_when<'a>(
for when_branch in branches {
let mono_expr = from_can(env, when_branch.value.value, procs, None);
let mono_guard = if let Some(loc_guard) = when_branch.guard {
Some(from_can(env, loc_guard.value, procs, None))
} else {
None
};
let guard = if mono_guard.is_some() {
let exhaustive_guard = if when_branch.guard.is_some() {
Guard::HasGuard
} else {
Guard::NoGuard
@ -1057,12 +1051,12 @@ fn from_can_when<'a>(
loc_branches.push((
Located::at(loc_pattern.region, mono_pattern.clone()),
guard.clone(),
exhaustive_guard.clone(),
));
let mut stores = Vec::with_capacity_in(1, env.arena);
let mono_expr = match store_pattern(
let (mono_guard, expr_with_stores) = match store_pattern(
env,
&mono_pattern,
cond_symbol,
@ -1070,12 +1064,52 @@ fn from_can_when<'a>(
&mut stores,
) {
Ok(_) => {
Expr::Store(stores.into_bump_slice(), env.arena.alloc(mono_expr.clone()))
// if the branch is guarded, the guard can use variables bound in the
// pattern. They must be available, so we give the stores to the
// decision_tree. A branch with guard can only be entered with the guard
// evaluated, so variables will also be loaded in the branch's body expr.
//
// otherwise, we modify the branch's expression to include the stores
if let Some(loc_guard) = when_branch.guard.clone() {
let expr = from_can(env, loc_guard.value, procs, None);
(
crate::decision_tree::Guard::Guard {
stores: stores.into_bump_slice(),
expr,
},
mono_expr.clone(),
)
} else {
(
crate::decision_tree::Guard::NoGuard,
Expr::Store(
stores.into_bump_slice(),
env.arena.alloc(mono_expr.clone()),
),
)
}
}
Err(message) => {
// when the pattern is invalid, a guard must give a runtime error too
if when_branch.guard.is_some() {
(
crate::decision_tree::Guard::Guard {
stores: &[],
expr: Expr::RuntimeError(env.arena.alloc(message)),
},
// we can never hit this
Expr::RuntimeError(&"invalid pattern with guard: unreachable"),
)
} else {
(
crate::decision_tree::Guard::NoGuard,
Expr::RuntimeError(env.arena.alloc(message)),
)
}
}
Err(message) => Expr::RuntimeError(env.arena.alloc(message)),
};
opt_branches.push((mono_pattern, mono_guard.clone(), mono_expr));
opt_branches.push((mono_pattern, mono_guard, expr_with_stores));
}
}