mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-21 15:59:20 +03:00
commit
7c61d0d278
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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: _,
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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(..)
|
||||
|
@ -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,
|
||||
..
|
||||
|
@ -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))),
|
||||
|
@ -60,16 +60,30 @@ fn headers_from_annotation_help(
|
||||
match pattern {
|
||||
Identifier(symbol)
|
||||
| Shadowed(_, _, symbol)
|
||||
// TODO(abilities): handle linking the member def to the specialization ident
|
||||
| AbilityMemberSpecialization {
|
||||
ident: symbol,
|
||||
specializes: _,
|
||||
} => {
|
||||
let annotation_index = { let typ = types.from_old_type(annotation.value); constraints.push_type(types, typ) };
|
||||
let annotation_index = {
|
||||
let typ = types.from_old_type(annotation.value);
|
||||
constraints.push_type(types, typ)
|
||||
};
|
||||
let typ = Loc::at(annotation.region, annotation_index);
|
||||
headers.insert(*symbol, typ);
|
||||
true
|
||||
}
|
||||
|
||||
As(subpattern, symbol) => {
|
||||
let annotation_index = {
|
||||
let typ = types.from_old_type(annotation.value);
|
||||
constraints.push_type(types, typ)
|
||||
};
|
||||
let typ = Loc::at(annotation.region, annotation_index);
|
||||
headers.insert(*symbol, typ);
|
||||
|
||||
headers_from_annotation_help(types, constraints, &subpattern.value, annotation, headers)
|
||||
}
|
||||
|
||||
Underscore
|
||||
| MalformedPattern(_, _)
|
||||
| UnsupportedPattern(_)
|
||||
@ -93,7 +107,10 @@ fn headers_from_annotation_help(
|
||||
// `{ x ? 0 } = rec` or `{ x: 5 } -> ...` in all cases
|
||||
// the type of `x` within the binding itself is the same.
|
||||
if let Some(field_type) = fields.get(&destruct.label) {
|
||||
let field_type_index = { let typ = types.from_old_type(&field_type.as_inner().clone()); constraints.push_type(types, typ) };
|
||||
let field_type_index = {
|
||||
let typ = types.from_old_type(&field_type.as_inner().clone());
|
||||
constraints.push_type(types, typ)
|
||||
};
|
||||
headers.insert(
|
||||
destruct.symbol,
|
||||
Loc::at(annotation.region, field_type_index),
|
||||
@ -108,13 +125,24 @@ 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
|
||||
},
|
||||
List { patterns, .. } => {
|
||||
if let Some((_, Some(rest))) = patterns.opt_rest {
|
||||
let annotation_index = {
|
||||
let typ = types.from_old_type(annotation.value);
|
||||
constraints.push_type(types, typ)
|
||||
};
|
||||
let typ = Loc::at(annotation.region, annotation_index);
|
||||
headers.insert(rest, typ);
|
||||
|
||||
false
|
||||
} else {
|
||||
// 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,
|
||||
@ -165,7 +193,10 @@ fn headers_from_annotation_help(
|
||||
&& type_arguments.len() == pat_type_arguments.len()
|
||||
&& lambda_set_variables.len() == pat_lambda_set_variables.len() =>
|
||||
{
|
||||
let annotation_index = { let typ = types.from_old_type(annotation.value); constraints.push_type(types, typ) };
|
||||
let annotation_index = {
|
||||
let typ = types.from_old_type(annotation.value);
|
||||
constraints.push_type(types, typ)
|
||||
};
|
||||
let typ = Loc::at(annotation.region, annotation_index);
|
||||
headers.insert(*opaque, typ);
|
||||
|
||||
@ -215,8 +246,7 @@ pub fn constrain_pattern(
|
||||
}
|
||||
|
||||
Identifier(symbol) | Shadowed(_, _, symbol) => {
|
||||
let expected = &constraints[expected];
|
||||
let type_index = *expected.get_type_ref();
|
||||
let type_index = *constraints[expected].get_type_ref();
|
||||
|
||||
if could_be_a_tag_union(types, type_index) {
|
||||
state
|
||||
@ -233,6 +263,29 @@ pub fn constrain_pattern(
|
||||
);
|
||||
}
|
||||
|
||||
As(subpattern, symbol) => {
|
||||
// NOTE: we don't use `could_be_a_tag_union` here. The `PATTERN as name` should
|
||||
// just use the type of the PATTERN, and not influence what type is inferred for PATTERN
|
||||
|
||||
state.headers.insert(
|
||||
*symbol,
|
||||
Loc {
|
||||
region,
|
||||
value: *constraints[expected].get_type_ref(),
|
||||
},
|
||||
);
|
||||
|
||||
constrain_pattern(
|
||||
types,
|
||||
constraints,
|
||||
env,
|
||||
&subpattern.value,
|
||||
subpattern.region,
|
||||
expected,
|
||||
state,
|
||||
)
|
||||
}
|
||||
|
||||
AbilityMemberSpecialization {
|
||||
ident: symbol,
|
||||
specializes: _,
|
||||
@ -554,14 +607,20 @@ pub fn constrain_pattern(
|
||||
List {
|
||||
list_var,
|
||||
elem_var,
|
||||
patterns:
|
||||
ListPatterns {
|
||||
patterns,
|
||||
opt_rest: _,
|
||||
},
|
||||
patterns: ListPatterns { patterns, opt_rest },
|
||||
} => {
|
||||
let elem_var_index = constraints.push_variable(*elem_var);
|
||||
|
||||
if let Some((_, Some(rest))) = opt_rest {
|
||||
state.headers.insert(
|
||||
*rest,
|
||||
Loc {
|
||||
region,
|
||||
value: *constraints[expected].get_type_ref(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
for loc_pat in patterns.iter() {
|
||||
let expected = constraints.push_pat_expected_type(PExpected::ForReason(
|
||||
PReason::ListElem,
|
||||
|
@ -2869,6 +2869,8 @@ fn pattern_to_when<'a>(
|
||||
(*new_symbol, Loc::at_zero(RuntimeError(error)))
|
||||
}
|
||||
|
||||
As(_, _) => todo!("as bindings are not supported yet"),
|
||||
|
||||
UnsupportedPattern(region) => {
|
||||
// create the runtime error here, instead of delegating to When.
|
||||
// UnsupportedPattern should then never occur in When
|
||||
@ -9390,6 +9392,7 @@ fn from_can_pattern_help<'a>(
|
||||
match can_pattern {
|
||||
Underscore => Ok(Pattern::Underscore),
|
||||
Identifier(symbol) => Ok(Pattern::Identifier(*symbol)),
|
||||
As(_, _) => todo!(),
|
||||
AbilityMemberSpecialization { ident, .. } => Ok(Pattern::Identifier(*ident)),
|
||||
IntLiteral(var, _, int_str, int, _bound) => Ok(make_num_literal_pattern(
|
||||
env,
|
||||
|
@ -8582,4 +8582,78 @@ mod solve_expr {
|
||||
@"polyDbg : a -[[polyDbg(1)]]-> a"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pattern_as_uses_inferred_type() {
|
||||
infer_queries!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
main = when A "foo" is
|
||||
A _ as a -> a
|
||||
# ^
|
||||
b -> b
|
||||
# ^
|
||||
"#
|
||||
),
|
||||
@r###"
|
||||
a : [A Str]*
|
||||
b : [A Str]*
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pattern_as_does_not_narrow() {
|
||||
infer_queries!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
input : [A Str, B Str]
|
||||
input = A "foo"
|
||||
|
||||
drop : a -> {}
|
||||
drop = \_ -> {}
|
||||
|
||||
main = when input is
|
||||
# ^^^^^
|
||||
A _ as a -> drop a
|
||||
# ^
|
||||
B _ as b -> drop b
|
||||
# ^
|
||||
"#
|
||||
),
|
||||
@r###"
|
||||
input : [A Str, B Str]
|
||||
a : [A Str, B Str]
|
||||
b : [A Str, B Str]
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pattern_as_list() {
|
||||
infer_queries!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
input : List Str
|
||||
input = [ "foo", "bar" ]
|
||||
|
||||
main = when input is
|
||||
# ^^^^^
|
||||
[ _first, .. as rest ] -> 1 + List.len rest
|
||||
# ^^^^
|
||||
[] -> 0
|
||||
"#
|
||||
),
|
||||
@r###"
|
||||
input : List Str
|
||||
rest : List Str
|
||||
"###
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user