mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-01 10:52:18 +00:00
Merge pull request #3616 from rtfeldman/i3614
Compile branches in the presence of degenerate patterns
This commit is contained in:
commit
d212dffa1a
14 changed files with 210 additions and 66 deletions
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
def::Def,
|
||||
expr::{AccessorData, ClosureData, Expr, Field, OpaqueWrapFunctionData},
|
||||
expr::{AccessorData, ClosureData, Expr, Field, OpaqueWrapFunctionData, WhenBranchPattern},
|
||||
pattern::{DestructType, Pattern, RecordDestruct},
|
||||
};
|
||||
use roc_module::{
|
||||
|
@ -295,7 +295,16 @@ fn deep_copy_expr_help<C: CopyEnv>(env: &mut C, copied: &mut Vec<Variable>, expr
|
|||
}| crate::expr::WhenBranch {
|
||||
patterns: patterns
|
||||
.iter()
|
||||
.map(|lp| lp.map(|p| deep_copy_pattern_help(env, copied, p)))
|
||||
.map(
|
||||
|WhenBranchPattern {
|
||||
pattern,
|
||||
degenerate,
|
||||
}| WhenBranchPattern {
|
||||
pattern: pattern
|
||||
.map(|p| deep_copy_pattern_help(env, copied, p)),
|
||||
degenerate: *degenerate,
|
||||
},
|
||||
)
|
||||
.collect(),
|
||||
value: value.map(|e| go_help!(e)),
|
||||
guard: guard.as_ref().map(|le| le.map(|e| go_help!(e))),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::annotation::IntroducedVariables;
|
||||
use crate::def::Def;
|
||||
use crate::expr::{AnnotatedMark, ClosureData, Declarations, Expr, Recursive};
|
||||
use crate::expr::{AnnotatedMark, ClosureData, Declarations, Expr, Recursive, WhenBranchPattern};
|
||||
use crate::pattern::Pattern;
|
||||
use crate::scope::Scope;
|
||||
use roc_collections::{SendMap, VecSet};
|
||||
|
@ -475,11 +475,15 @@ fn build_effect_after(
|
|||
type_arguments,
|
||||
lambda_set_variables,
|
||||
};
|
||||
let pattern = WhenBranchPattern {
|
||||
pattern: Loc::at_zero(pattern),
|
||||
degenerate: false,
|
||||
};
|
||||
|
||||
let branches = vec![crate::expr::WhenBranch {
|
||||
guard: None,
|
||||
value: Loc::at_zero(force_inner_thunk_call),
|
||||
patterns: vec![Loc::at_zero(pattern)],
|
||||
patterns: vec![pattern],
|
||||
redundant: RedundantMark::new(var_store),
|
||||
}];
|
||||
|
||||
|
@ -1256,9 +1260,13 @@ fn build_effect_loop_inner_body(
|
|||
let step_tag_name = TagName("Step".into());
|
||||
|
||||
let step_pattern = applied_tag_pattern(step_tag_name, &[new_state_symbol], var_store);
|
||||
let step_pattern = WhenBranchPattern {
|
||||
pattern: Loc::at_zero(step_pattern),
|
||||
degenerate: false,
|
||||
};
|
||||
|
||||
crate::expr::WhenBranch {
|
||||
patterns: vec![Loc::at_zero(step_pattern)],
|
||||
patterns: vec![step_pattern],
|
||||
value: Loc::at_zero(force_thunk2),
|
||||
guard: None,
|
||||
redundant: RedundantMark::new(var_store),
|
||||
|
@ -1268,9 +1276,13 @@ fn build_effect_loop_inner_body(
|
|||
let done_branch = {
|
||||
let done_tag_name = TagName("Done".into());
|
||||
let done_pattern = applied_tag_pattern(done_tag_name, &[done_symbol], var_store);
|
||||
let done_pattern = WhenBranchPattern {
|
||||
pattern: Loc::at_zero(done_pattern),
|
||||
degenerate: false,
|
||||
};
|
||||
|
||||
crate::expr::WhenBranch {
|
||||
patterns: vec![Loc::at_zero(done_pattern)],
|
||||
patterns: vec![done_pattern],
|
||||
value: Loc::at_zero(Expr::Var(done_symbol)),
|
||||
guard: None,
|
||||
redundant: RedundantMark::new(var_store),
|
||||
|
|
|
@ -260,16 +260,19 @@ pub fn sketch_when_branches(
|
|||
// NB: ordering the guard pattern first seems to be better at catching
|
||||
// non-exhaustive constructors in the second argument; see the paper to see if
|
||||
// there is a way to improve this in general.
|
||||
vec![guard_pattern, sketch_pattern(target_var, &loc_pat.value)],
|
||||
vec![
|
||||
guard_pattern,
|
||||
sketch_pattern(target_var, &loc_pat.pattern.value),
|
||||
],
|
||||
)]
|
||||
} else {
|
||||
// Simple case
|
||||
vec![sketch_pattern(target_var, &loc_pat.value)]
|
||||
vec![sketch_pattern(target_var, &loc_pat.pattern.value)]
|
||||
};
|
||||
|
||||
let row = SketchedRow {
|
||||
patterns,
|
||||
region: loc_pat.region,
|
||||
region: loc_pat.pattern.region,
|
||||
guard,
|
||||
redundant_mark: *redundant,
|
||||
};
|
||||
|
|
|
@ -481,9 +481,18 @@ impl Recursive {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct WhenBranchPattern {
|
||||
pub pattern: Loc<Pattern>,
|
||||
/// Degenerate branch patterns are those that don't fully bind symbols that the branch body
|
||||
/// needs. For example, in `A x | B y -> x`, the `B y` pattern is degenerate.
|
||||
/// Degenerate patterns emit a runtime error if reached in a program.
|
||||
pub degenerate: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct WhenBranch {
|
||||
pub patterns: Vec<Loc<Pattern>>,
|
||||
pub patterns: Vec<WhenBranchPattern>,
|
||||
pub value: Loc<Expr>,
|
||||
pub guard: Option<Loc<Expr>>,
|
||||
/// Whether this branch is redundant in the `when` it appears in
|
||||
|
@ -497,11 +506,13 @@ impl WhenBranch {
|
|||
.patterns
|
||||
.first()
|
||||
.expect("when branch has no pattern?")
|
||||
.pattern
|
||||
.region,
|
||||
&self
|
||||
.patterns
|
||||
.last()
|
||||
.expect("when branch has no pattern?")
|
||||
.pattern
|
||||
.region,
|
||||
)
|
||||
}
|
||||
|
@ -512,7 +523,7 @@ impl WhenBranch {
|
|||
Region::across_all(
|
||||
self.patterns
|
||||
.iter()
|
||||
.map(|p| &p.region)
|
||||
.map(|p| &p.pattern.region)
|
||||
.chain([self.value.region].iter()),
|
||||
)
|
||||
}
|
||||
|
@ -1350,14 +1361,20 @@ fn canonicalize_when_branch<'a>(
|
|||
);
|
||||
|
||||
multi_pattern_variables.add_pattern(&can_pattern);
|
||||
patterns.push(can_pattern);
|
||||
patterns.push(WhenBranchPattern {
|
||||
pattern: can_pattern,
|
||||
degenerate: false,
|
||||
});
|
||||
}
|
||||
|
||||
let mut some_symbols_not_bound_in_all_patterns = false;
|
||||
for (unbound_symbol, region) in multi_pattern_variables.get_unbound() {
|
||||
env.problem(Problem::NotBoundInAllPatterns {
|
||||
unbound_symbol,
|
||||
region,
|
||||
})
|
||||
});
|
||||
|
||||
some_symbols_not_bound_in_all_patterns = true;
|
||||
}
|
||||
|
||||
let (value, mut branch_output) = canonicalize_expr(
|
||||
|
@ -1384,12 +1401,33 @@ fn canonicalize_when_branch<'a>(
|
|||
|
||||
// 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 BindingsFromPattern::new_many(patterns.iter()) {
|
||||
if !output.references.has_value_lookup(symbol) {
|
||||
let mut pattern_bound_symbols_body_needs = VecSet::default();
|
||||
for (symbol, region) in BindingsFromPattern::new_many(patterns.iter().map(|pat| &pat.pattern)) {
|
||||
if output.references.has_value_lookup(symbol) {
|
||||
pattern_bound_symbols_body_needs.insert(symbol);
|
||||
} else {
|
||||
env.problem(Problem::UnusedDef(symbol, region));
|
||||
}
|
||||
}
|
||||
|
||||
if some_symbols_not_bound_in_all_patterns && !pattern_bound_symbols_body_needs.is_empty() {
|
||||
// There might be branches that don't bind all the symbols needed by the body; mark those
|
||||
// branches degenerate.
|
||||
for pattern in patterns.iter_mut() {
|
||||
let bound_by_pattern: VecSet<_> = BindingsFromPattern::new(&pattern.pattern)
|
||||
.map(|(sym, _)| sym)
|
||||
.collect();
|
||||
|
||||
let binds_all_needed = pattern_bound_symbols_body_needs
|
||||
.iter()
|
||||
.all(|sym| bound_by_pattern.contains(sym));
|
||||
|
||||
if !binds_all_needed {
|
||||
pattern.degenerate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
WhenBranch {
|
||||
patterns,
|
||||
|
|
|
@ -910,7 +910,10 @@ fn fix_values_captured_in_closure_expr(
|
|||
|
||||
// patterns can contain default expressions, so much go over them too!
|
||||
for loc_pat in branch.patterns.iter_mut() {
|
||||
fix_values_captured_in_closure_pattern(&mut loc_pat.value, no_capture_symbols);
|
||||
fix_values_captured_in_closure_pattern(
|
||||
&mut loc_pat.pattern.value,
|
||||
no_capture_symbols,
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(guard) = &mut branch.guard {
|
||||
|
|
|
@ -323,9 +323,13 @@ pub fn walk_when_branch<V: Visitor>(
|
|||
redundant: _,
|
||||
} = branch;
|
||||
|
||||
patterns
|
||||
.iter()
|
||||
.for_each(|pat| visitor.visit_pattern(&pat.value, pat.region, pat.value.opt_var()));
|
||||
patterns.iter().for_each(|pat| {
|
||||
visitor.visit_pattern(
|
||||
&pat.pattern.value,
|
||||
pat.pattern.region,
|
||||
pat.pattern.value.opt_var(),
|
||||
)
|
||||
});
|
||||
visitor.visit_expr(&value.value, value.region, expr_var);
|
||||
if let Some(guard) = guard {
|
||||
visitor.visit_expr(&guard.value, guard.region, Variable::BOOL);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue