mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-03 19:58:18 +00:00
Canonicalization of list patterns
This commit is contained in:
parent
55ea3bbea2
commit
b0a8b85de3
13 changed files with 189 additions and 4 deletions
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
def::Def,
|
||||
expr::{AccessorData, ClosureData, Expr, Field, OpaqueWrapFunctionData, WhenBranchPattern},
|
||||
pattern::{DestructType, Pattern, RecordDestruct},
|
||||
pattern::{DestructType, ListPatterns, Pattern, RecordDestruct},
|
||||
};
|
||||
use roc_module::{
|
||||
ident::{Lowercase, TagName},
|
||||
|
@ -707,6 +707,18 @@ fn deep_copy_pattern_help<C: CopyEnv>(
|
|||
})
|
||||
.collect(),
|
||||
},
|
||||
List {
|
||||
list_var,
|
||||
elem_var,
|
||||
patterns: ListPatterns { patterns, opt_rest },
|
||||
} => List {
|
||||
list_var: sub!(*list_var),
|
||||
elem_var: sub!(*elem_var),
|
||||
patterns: ListPatterns {
|
||||
patterns: patterns.iter().map(|lp| lp.map(|p| go_help!(p))).collect(),
|
||||
opt_rest: *opt_rest,
|
||||
},
|
||||
},
|
||||
NumLiteral(var, s, n, bound) => NumLiteral(sub!(*var), s.clone(), *n, *bound),
|
||||
IntLiteral(v1, v2, s, n, bound) => IntLiteral(sub!(*v1), sub!(*v2), s.clone(), *n, *bound),
|
||||
FloatLiteral(v1, v2, s, n, bound) => {
|
||||
|
|
|
@ -1921,6 +1921,14 @@ fn pattern_to_vars_by_symbol(
|
|||
}
|
||||
}
|
||||
|
||||
List {
|
||||
patterns, elem_var, ..
|
||||
} => {
|
||||
for pat in patterns.patterns.iter() {
|
||||
pattern_to_vars_by_symbol(vars_by_symbol, &pat.value, *elem_var);
|
||||
}
|
||||
}
|
||||
|
||||
NumLiteral(..)
|
||||
| IntLiteral(..)
|
||||
| FloatLiteral(..)
|
||||
|
|
|
@ -286,6 +286,8 @@ fn sketch_pattern(pattern: &crate::pattern::Pattern) -> SketchedPattern {
|
|||
SP::KnownCtor(union, IndexCtor::Record, tag_id, patterns)
|
||||
}
|
||||
|
||||
List { .. } => todo!(),
|
||||
|
||||
AppliedTag {
|
||||
tag_name,
|
||||
arguments,
|
||||
|
|
|
@ -898,6 +898,15 @@ fn fix_values_captured_in_closure_pattern(
|
|||
}
|
||||
}
|
||||
}
|
||||
List { patterns, .. } => {
|
||||
for loc_pat in patterns.patterns.iter_mut() {
|
||||
fix_values_captured_in_closure_pattern(
|
||||
&mut loc_pat.value,
|
||||
no_capture_symbols,
|
||||
closure_captures,
|
||||
);
|
||||
}
|
||||
}
|
||||
Identifier(_)
|
||||
| NumLiteral(..)
|
||||
| IntLiteral(..)
|
||||
|
|
|
@ -56,6 +56,11 @@ pub enum Pattern {
|
|||
ext_var: Variable,
|
||||
destructs: Vec<Loc<RecordDestruct>>,
|
||||
},
|
||||
List {
|
||||
list_var: Variable,
|
||||
elem_var: Variable,
|
||||
patterns: ListPatterns,
|
||||
},
|
||||
NumLiteral(Variable, Box<str>, IntValue, NumBound),
|
||||
IntLiteral(Variable, Variable, Box<str>, IntValue, IntBound),
|
||||
FloatLiteral(Variable, Variable, Box<str>, f64, FloatBound),
|
||||
|
@ -92,6 +97,10 @@ impl Pattern {
|
|||
AppliedTag { whole_var, .. } => Some(*whole_var),
|
||||
UnwrappedOpaque { whole_var, .. } => Some(*whole_var),
|
||||
RecordDestructure { whole_var, .. } => Some(*whole_var),
|
||||
List {
|
||||
list_var: whole_var,
|
||||
..
|
||||
} => Some(*whole_var),
|
||||
NumLiteral(var, ..) => Some(*var),
|
||||
IntLiteral(var, ..) => Some(*var),
|
||||
FloatLiteral(var, ..) => Some(*var),
|
||||
|
@ -119,6 +128,7 @@ impl Pattern {
|
|||
| MalformedPattern(..)
|
||||
| AbilityMemberSpecialization { .. } => true,
|
||||
RecordDestructure { destructs, .. } => destructs.is_empty(),
|
||||
List { patterns, .. } => patterns.surely_exhaustive(),
|
||||
AppliedTag { .. }
|
||||
| NumLiteral(..)
|
||||
| IntLiteral(..)
|
||||
|
@ -145,6 +155,7 @@ impl Pattern {
|
|||
UnwrappedOpaque { opaque, .. } => C::Opaque(*opaque),
|
||||
RecordDestructure { destructs, .. } if destructs.is_empty() => C::EmptyRecord,
|
||||
RecordDestructure { .. } => C::Record,
|
||||
List { .. } => C::List,
|
||||
NumLiteral(..) => C::Num,
|
||||
IntLiteral(..) => C::Int,
|
||||
FloatLiteral(..) => C::Float,
|
||||
|
@ -161,6 +172,25 @@ impl Pattern {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ListPatterns {
|
||||
pub patterns: Vec<Loc<Pattern>>,
|
||||
/// Where a rest pattern splits patterns before and after it, if it does at all.
|
||||
/// If present, patterns at index >= the rest index appear after the rest pattern.
|
||||
/// For example:
|
||||
/// [ .., A, B ] -> patterns = [A, B], rest = 0
|
||||
/// [ A, .., B ] -> patterns = [A, B], rest = 1
|
||||
/// [ A, B, .. ] -> patterns = [A, B], rest = 2
|
||||
pub opt_rest: Option<usize>,
|
||||
}
|
||||
|
||||
impl ListPatterns {
|
||||
/// Is this list pattern the trivially-exhaustive pattern `[..]`?
|
||||
fn surely_exhaustive(&self) -> bool {
|
||||
self.patterns.is_empty() && matches!(self.opt_rest, Some(0))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RecordDestruct {
|
||||
pub var: Variable,
|
||||
|
@ -621,8 +651,75 @@ pub fn canonicalize_pattern<'a>(
|
|||
unreachable!("should have been handled in RecordDestructure");
|
||||
}
|
||||
|
||||
List(..) => todo!(),
|
||||
ListRest => todo!(),
|
||||
List(patterns) => {
|
||||
// We want to admit the following cases:
|
||||
//
|
||||
// []
|
||||
// [..]
|
||||
// [.., P_1,* P_n]
|
||||
// [P_1,* P_n, ..]
|
||||
// [P_1,* P_m, .., P_n,* P_q]
|
||||
// [P_1,* P_n]
|
||||
//
|
||||
// So, a list-rest pattern can appear anywhere in a list pattern, but can appear at
|
||||
// most once.
|
||||
let elem_var = var_store.fresh();
|
||||
let list_var = var_store.fresh();
|
||||
|
||||
let mut rest_index = None;
|
||||
let mut can_pats = Vec::with_capacity(patterns.len());
|
||||
let mut opt_erroneous = None;
|
||||
|
||||
for (i, loc_pattern) in patterns.iter().enumerate() {
|
||||
match &loc_pattern.value {
|
||||
ListRest => match rest_index {
|
||||
None => {
|
||||
rest_index = Some(i);
|
||||
}
|
||||
Some(_) => {
|
||||
env.problem(Problem::MultipleListRestPattern {
|
||||
region: loc_pattern.region,
|
||||
});
|
||||
|
||||
opt_erroneous = Some(Pattern::MalformedPattern(
|
||||
MalformedPatternProblem::DuplicateListRestPattern,
|
||||
loc_pattern.region,
|
||||
));
|
||||
}
|
||||
},
|
||||
pattern => {
|
||||
let pat = canonicalize_pattern(
|
||||
env,
|
||||
var_store,
|
||||
scope,
|
||||
output,
|
||||
pattern_type,
|
||||
pattern,
|
||||
loc_pattern.region,
|
||||
permit_shadows,
|
||||
);
|
||||
can_pats.push(pat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we encountered an erroneous pattern (e.g. one with shadowing),
|
||||
// use the resulting RuntimeError. Otherwise, return a successful record destructure.
|
||||
opt_erroneous.unwrap_or(Pattern::List {
|
||||
list_var,
|
||||
elem_var,
|
||||
patterns: ListPatterns {
|
||||
patterns: can_pats,
|
||||
opt_rest: rest_index,
|
||||
},
|
||||
})
|
||||
}
|
||||
ListRest => {
|
||||
// Parsing should make sure these only appear in list patterns, where we will generate
|
||||
// better contextual errors.
|
||||
let problem = MalformedPatternProblem::Unknown;
|
||||
malformed_pattern(env, problem, region)
|
||||
}
|
||||
|
||||
Malformed(_str) => {
|
||||
let problem = MalformedPatternProblem::Unknown;
|
||||
|
@ -739,6 +836,9 @@ impl<'a> BindingsFromPattern<'a> {
|
|||
| MalformedPattern(_, _)
|
||||
| UnsupportedPattern(_)
|
||||
| OpaqueNotInScope(..) => (),
|
||||
List { patterns, .. } => {
|
||||
stack.extend(patterns.patterns.iter().rev().map(Pattern));
|
||||
}
|
||||
}
|
||||
}
|
||||
BindingsFromPatternWork::Destruct(loc_destruct) => {
|
||||
|
|
|
@ -472,6 +472,12 @@ pub fn walk_pattern<V: Visitor>(visitor: &mut V, pattern: &Pattern) {
|
|||
RecordDestructure { destructs, .. } => destructs
|
||||
.iter()
|
||||
.for_each(|d| visitor.visit_record_destruct(&d.value, d.region)),
|
||||
List {
|
||||
patterns, elem_var, ..
|
||||
} => patterns
|
||||
.patterns
|
||||
.iter()
|
||||
.for_each(|p| visitor.visit_pattern(&p.value, p.region, Some(*elem_var))),
|
||||
NumLiteral(..) => { /* terminal */ }
|
||||
IntLiteral(..) => { /* terminal */ }
|
||||
FloatLiteral(..) => { /* terminal */ }
|
||||
|
|
|
@ -101,6 +101,14 @@ fn headers_from_annotation_help(
|
|||
_ => false,
|
||||
},
|
||||
|
||||
List { .. } => {
|
||||
// There are no interesting headers to introduce for list patterns, since the only
|
||||
// exhaustive list pattern is
|
||||
// \[..] -> <body>
|
||||
// which does not introduce any symbols.
|
||||
false
|
||||
},
|
||||
|
||||
AppliedTag {
|
||||
tag_name,
|
||||
arguments,
|
||||
|
@ -501,6 +509,18 @@ pub fn constrain_pattern(
|
|||
state.constraints.push(whole_con);
|
||||
state.constraints.push(record_con);
|
||||
}
|
||||
|
||||
List {
|
||||
list_var,
|
||||
elem_var,
|
||||
patterns: _,
|
||||
} => {
|
||||
state.vars.push(*list_var);
|
||||
state.vars.push(*elem_var);
|
||||
|
||||
todo!();
|
||||
}
|
||||
|
||||
AppliedTag {
|
||||
whole_var,
|
||||
ext_var,
|
||||
|
|
|
@ -2703,7 +2703,7 @@ fn pattern_to_when<'a>(
|
|||
) -> (Symbol, Loc<roc_can::expr::Expr>) {
|
||||
use roc_can::expr::Expr::*;
|
||||
use roc_can::expr::{WhenBranch, WhenBranchPattern};
|
||||
use roc_can::pattern::Pattern::*;
|
||||
use roc_can::pattern::Pattern::{self, *};
|
||||
|
||||
match &pattern.value {
|
||||
Identifier(symbol) => (*symbol, body),
|
||||
|
@ -2766,6 +2766,8 @@ fn pattern_to_when<'a>(
|
|||
(symbol, Loc::at_zero(wrapped_body))
|
||||
}
|
||||
|
||||
Pattern::List { .. } => todo!(),
|
||||
|
||||
IntLiteral(..)
|
||||
| NumLiteral(..)
|
||||
| FloatLiteral(..)
|
||||
|
@ -9604,6 +9606,8 @@ fn from_can_pattern_help<'a>(
|
|||
field_layouts.into_bump_slice(),
|
||||
))
|
||||
}
|
||||
|
||||
List { .. } => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -185,6 +185,9 @@ pub enum Problem {
|
|||
UnnecessaryOutputWildcard {
|
||||
region: Region,
|
||||
},
|
||||
MultipleListRestPattern {
|
||||
region: Region,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
@ -375,4 +378,5 @@ pub enum MalformedPatternProblem {
|
|||
BadIdent(roc_parse::ident::BadIdent),
|
||||
EmptySingleQuote,
|
||||
MultipleCharsInSingleQuote,
|
||||
DuplicateListRestPattern,
|
||||
}
|
||||
|
|
|
@ -387,6 +387,7 @@ fn pattern<'a>(
|
|||
)
|
||||
.append(f.text("}"))
|
||||
.group(),
|
||||
List { .. } => todo!(),
|
||||
NumLiteral(_, n, _, _) | IntLiteral(_, _, n, _, _) | FloatLiteral(_, _, n, _, _) => {
|
||||
f.text(&**n)
|
||||
}
|
||||
|
|
|
@ -2296,6 +2296,7 @@ pub enum Category {
|
|||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum PatternCategory {
|
||||
Record,
|
||||
List,
|
||||
EmptyRecord,
|
||||
PatternGuard,
|
||||
PatternDefault,
|
||||
|
|
|
@ -1024,6 +1024,19 @@ pub fn can_problem<'b>(
|
|||
title = "UNNECESSARY WILDCARD".to_string();
|
||||
severity = Severity::Warning;
|
||||
}
|
||||
Problem::MultipleListRestPattern { region } => {
|
||||
doc = alloc.stack([
|
||||
alloc.reflow("This list pattern match has multiple rest patterns:"),
|
||||
alloc.region(lines.convert_region(region)),
|
||||
alloc.concat([
|
||||
alloc.reflow("I only support compiling list patterns with one "),
|
||||
alloc.parser_suggestion(".."),
|
||||
alloc.reflow(" pattern! Can you remove this additional one?"),
|
||||
]),
|
||||
]);
|
||||
title = "MULTIPLE LIST REST PATTERNS".to_string();
|
||||
severity = Severity::RuntimeError;
|
||||
}
|
||||
};
|
||||
|
||||
Report {
|
||||
|
@ -1521,6 +1534,7 @@ fn pretty_runtime_error<'b>(
|
|||
QualifiedIdentifier => " qualified ",
|
||||
EmptySingleQuote => " empty character literal ",
|
||||
MultipleCharsInSingleQuote => " overfull literal ",
|
||||
DuplicateListRestPattern => " second rest pattern ",
|
||||
};
|
||||
|
||||
let tip = match problem {
|
||||
|
@ -1533,6 +1547,9 @@ fn pretty_runtime_error<'b>(
|
|||
QualifiedIdentifier => alloc
|
||||
.tip()
|
||||
.append(alloc.reflow("In patterns, only tags can be qualified")),
|
||||
DuplicateListRestPattern => alloc
|
||||
.tip()
|
||||
.append(alloc.reflow("List patterns can only have one rest pattern")),
|
||||
};
|
||||
|
||||
doc = alloc.stack([
|
||||
|
|
|
@ -1993,6 +1993,7 @@ fn add_pattern_category<'b>(
|
|||
PatternDefault => alloc.reflow(" an optional field of type:"),
|
||||
Set => alloc.reflow(" sets of type:"),
|
||||
Map => alloc.reflow(" maps of type:"),
|
||||
List => alloc.reflow(" lists of type:"),
|
||||
Ctor(tag_name) => alloc.concat([
|
||||
alloc.reflow(" a "),
|
||||
alloc.tag_name(tag_name.clone()),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue