Merge pull request #4843 from roc-lang/pattern-as-can

Pattern as can
This commit is contained in:
Ayaz 2023-01-08 19:36:40 -06:00 committed by GitHub
commit 7c61d0d278
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 273 additions and 47 deletions

View file

@ -654,6 +654,7 @@ fn deep_copy_pattern_help<C: CopyEnv>(
match pat {
Identifier(s) => Identifier(*s),
As(subpattern, s) => As(Box::new(subpattern.map(|p| go_help!(p))), *s),
AppliedTag {
whole_var,
ext_var,

View file

@ -431,6 +431,9 @@ fn pattern<'a>(
| AbilityMemberSpecialization {
specializes: sym, ..
} => pp_sym(c, f, *sym),
As(subpattern, symbol) => pattern(c, prec, f, &subpattern.value)
.append(f.text(" as "))
.append(pp_sym(c, f, *symbol)),
AppliedTag {
tag_name,
arguments,

View file

@ -1967,6 +1967,12 @@ fn pattern_to_vars_by_symbol(
vars_by_symbol.insert(*symbol, expr_var);
}
As(subpattern, symbol) => {
vars_by_symbol.insert(*symbol, expr_var);
pattern_to_vars_by_symbol(vars_by_symbol, &subpattern.value, expr_var);
}
AbilityMemberSpecialization {
ident,
specializes: _,

View file

@ -301,6 +301,7 @@ fn sketch_pattern(pattern: &crate::pattern::Pattern) -> SketchedPattern {
use SketchedPattern as SP;
match pattern {
As(subpattern, _) => sketch_pattern(&subpattern.value),
&NumLiteral(_, _, IntValue::I128(n), _) | &IntLiteral(_, _, _, IntValue::I128(n), _) => {
SP::Literal(Literal::Int(n))
}

View file

@ -927,6 +927,14 @@ fn fix_values_captured_in_closure_pattern(
);
}
}
As(subpattern, _) => {
fix_values_captured_in_closure_pattern(
&mut subpattern.value,
no_capture_symbols,
closure_captures,
);
}
Identifier(_)
| NumLiteral(..)
| IntLiteral(..)

View file

@ -22,6 +22,7 @@ use roc_types::types::{LambdaSet, OptAbleVar, PatternCategory, Type};
#[derive(Clone, Debug)]
pub enum Pattern {
Identifier(Symbol),
As(Box<Loc<Pattern>>, Symbol),
AppliedTag {
whole_var: Variable,
ext_var: Variable,
@ -94,6 +95,7 @@ impl Pattern {
use Pattern::*;
match self {
Identifier(_) => None,
As(pattern, _) => pattern.value.opt_var(),
AppliedTag { whole_var, .. } => Some(*whole_var),
UnwrappedOpaque { whole_var, .. } => Some(*whole_var),
@ -129,6 +131,7 @@ impl Pattern {
| MalformedPattern(..)
| AbilityMemberSpecialization { .. } => true,
RecordDestructure { destructs, .. } => destructs.is_empty(),
As(pattern, _identifier) => pattern.value.surely_exhaustive(),
List { patterns, .. } => patterns.surely_exhaustive(),
AppliedTag { .. }
| NumLiteral(..)
@ -151,6 +154,7 @@ impl Pattern {
match self {
Identifier(_) => C::PatternDefault,
As(pattern, _) => pattern.value.category(),
AppliedTag { tag_name, .. } => C::Ctor(tag_name.clone()),
UnwrappedOpaque { opaque, .. } => C::Opaque(*opaque),
@ -182,18 +186,18 @@ pub struct ListPatterns {
/// [ .., 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>,
pub opt_rest: Option<(usize, Option<Symbol>)>,
}
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))
self.patterns.is_empty() && matches!(self.opt_rest, Some((0, _)))
}
pub fn arity(&self) -> ListArity {
match self.opt_rest {
Some(i) => {
Some((i, _)) => {
let before = i;
let after = self.patterns.len() - before;
ListArity::Slice(before, after)
@ -289,6 +293,43 @@ pub fn canonicalize_def_header_pattern<'a>(
#[derive(PartialEq, Eq, Clone, Copy)]
pub struct PermitShadows(pub bool);
fn canonicalize_pattern_symbol<'a>(
env: &mut Env<'a>,
scope: &mut Scope,
output: &mut Output,
region: Region,
permit_shadows: PermitShadows,
name: &str,
) -> Result<Symbol, Pattern> {
match scope.introduce_str(name, region) {
Ok(symbol) => {
output.references.insert_bound(symbol);
Ok(symbol)
}
Err((shadowed_symbol, shadow, new_symbol)) => {
if permit_shadows.0 {
output.references.insert_bound(shadowed_symbol.value);
Ok(shadowed_symbol.value)
} else {
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
original_region: shadowed_symbol.region,
shadow: shadow.clone(),
kind: ShadowKind::Variable,
}));
output.references.insert_bound(new_symbol);
Err(Pattern::Shadowed(
shadowed_symbol.region,
shadow,
new_symbol,
))
}
}
}
}
#[allow(clippy::too_many_arguments)]
pub fn canonicalize_pattern<'a>(
env: &mut Env<'a>,
@ -304,29 +345,12 @@ pub fn canonicalize_pattern<'a>(
use PatternType::*;
let can_pattern = match pattern {
Identifier(name) => match scope.introduce_str(name, region) {
Ok(symbol) => {
output.references.insert_bound(symbol);
Pattern::Identifier(symbol)
Identifier(name) => {
match canonicalize_pattern_symbol(env, scope, output, region, permit_shadows, name) {
Ok(symbol) => Pattern::Identifier(symbol),
Err(pattern) => pattern,
}
Err((shadowed_symbol, shadow, new_symbol)) => {
if permit_shadows.0 {
output.references.insert_bound(shadowed_symbol.value);
Pattern::Identifier(shadowed_symbol.value)
} else {
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
original_region: shadowed_symbol.region,
shadow: shadow.clone(),
kind: ShadowKind::Variable,
}));
output.references.insert_bound(new_symbol);
Pattern::Shadowed(shadowed_symbol.region, shadow, new_symbol)
}
}
},
}
Tag(name) => {
// Canonicalize the tag's name.
Pattern::AppliedTag {
@ -684,14 +708,33 @@ pub fn canonicalize_pattern<'a>(
let list_var = var_store.fresh();
let mut rest_index = None;
let mut rest_name = 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(_opt_pattern_as) => match rest_index {
ListRest(opt_pattern_as) => match rest_index {
None => {
rest_index = Some(i);
if let Some((_, pattern_as)) = opt_pattern_as {
match canonicalize_pattern_symbol(
env,
scope,
output,
region,
permit_shadows,
pattern_as.identifier.value,
) {
Ok(symbol) => {
rest_name = Some(symbol);
}
Err(pattern) => {
opt_erroneous = Some(pattern);
}
}
}
}
Some(_) => {
env.problem(Problem::MultipleListRestPattern {
@ -727,7 +770,7 @@ pub fn canonicalize_pattern<'a>(
elem_var,
patterns: ListPatterns {
patterns: can_pats,
opt_rest: rest_index,
opt_rest: rest_index.map(|i| (i, rest_name)),
},
})
}
@ -738,8 +781,29 @@ pub fn canonicalize_pattern<'a>(
malformed_pattern(env, problem, region)
}
As(_pattern, _pattern_as) => {
todo!();
As(loc_pattern, pattern_as) => {
let can_subpattern = canonicalize_pattern(
env,
var_store,
scope,
output,
pattern_type,
&loc_pattern.value,
loc_pattern.region,
permit_shadows,
);
match canonicalize_pattern_symbol(
env,
scope,
output,
region,
permit_shadows,
pattern_as.identifier.value,
) {
Ok(symbol) => Pattern::As(Box::new(can_subpattern), symbol),
Err(pattern) => pattern,
}
}
Malformed(_str) => {
@ -832,6 +896,10 @@ impl<'a> BindingsFromPattern<'a> {
} => {
return Some((*symbol, loc_pattern.region));
}
As(pattern, symbol) => {
stack.push(Pattern(pattern));
return Some((*symbol, loc_pattern.region));
}
AppliedTag {
arguments: loc_args,
..

View file

@ -476,6 +476,9 @@ pub fn walk_pattern<V: Visitor>(visitor: &mut V, pattern: &Pattern) {
match pattern {
Identifier(..) => { /* terminal */ }
As(subpattern, _symbol) => {
visitor.visit_pattern(&subpattern.value, subpattern.region, None)
}
AppliedTag { arguments, .. } => arguments
.iter()
.for_each(|(v, lp)| visitor.visit_pattern(&lp.value, lp.region, Some(*v))),