mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 16:44:33 +00:00
typecheck tags with arguments
also fixes an issue where private tags in patterns would not have the @ symbol in their name
This commit is contained in:
parent
98515cdf23
commit
dd240cf53a
7 changed files with 104 additions and 29 deletions
|
@ -17,7 +17,7 @@ use im_rc::Vector;
|
|||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Pattern {
|
||||
Identifier(Symbol),
|
||||
AppliedTag(Variable, Symbol, Vec<Located<Pattern>>),
|
||||
AppliedTag(Variable, Symbol, Vec<(Variable, Located<Pattern>)>),
|
||||
IntLiteral(i64),
|
||||
FloatLiteral(f64),
|
||||
StrLiteral(Box<str>),
|
||||
|
@ -94,6 +94,32 @@ pub fn canonicalize_pattern<'a>(
|
|||
vec![],
|
||||
)
|
||||
}
|
||||
&Apply(tag, patterns) => {
|
||||
let mut can_patterns = Vec::with_capacity(patterns.len());
|
||||
for loc_pattern in *patterns {
|
||||
can_patterns.push((
|
||||
var_store.fresh(),
|
||||
canonicalize_pattern(
|
||||
env,
|
||||
var_store,
|
||||
scope,
|
||||
pattern_type,
|
||||
&loc_pattern.value,
|
||||
loc_pattern.region,
|
||||
shadowable_idents,
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
let symbol = match tag.value {
|
||||
GlobalTag(name) => Symbol::from_global_tag(name),
|
||||
PrivateTag(name) => Symbol::from_private_tag(&env.home, name),
|
||||
_ => unreachable!("Other patterns cannot be applied"),
|
||||
};
|
||||
|
||||
Pattern::AppliedTag(var_store.fresh(), symbol, can_patterns)
|
||||
}
|
||||
|
||||
&FloatLiteral(ref string) => match pattern_type {
|
||||
WhenBranch => {
|
||||
let float = finish_parsing_float(string)
|
||||
|
@ -344,13 +370,10 @@ pub fn remove_idents(pattern: &ast::Pattern, idents: &mut ImMap<Ident, (Symbol,
|
|||
QualifiedIdentifier(_name) => {
|
||||
panic!("TODO implement QualifiedIdentifier pattern in remove_idents.");
|
||||
}
|
||||
Apply(_, _) => {
|
||||
panic!("TODO implement Apply pattern in remove_idents.");
|
||||
// AppliedVariant(_, Some(loc_args)) => {
|
||||
// for loc_arg in loc_args {
|
||||
// remove_idents(loc_arg.value, idents);
|
||||
// }
|
||||
// }
|
||||
Apply(_, patterns) => {
|
||||
for loc_pattern in *patterns {
|
||||
remove_idents(&loc_pattern.value, idents);
|
||||
}
|
||||
}
|
||||
RecordDestructure(patterns) => {
|
||||
for loc_pattern in patterns {
|
||||
|
@ -410,16 +433,10 @@ fn add_idents_from_pattern<'a>(
|
|||
QualifiedIdentifier(_name) => {
|
||||
panic!("TODO implement QualifiedIdentifier pattern.");
|
||||
}
|
||||
Apply(_, _) => {
|
||||
panic!("TODO implement Apply pattern.");
|
||||
// &AppliedVariant(_, ref opt_loc_args) => match opt_loc_args {
|
||||
// &None => (),
|
||||
// &Some(ref loc_args) => {
|
||||
// for loc_arg in loc_args.iter() {
|
||||
// add_idents_from_pattern(loc_arg, scope, answer);
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
Apply(_tag, patterns) => {
|
||||
for loc_pattern in *patterns {
|
||||
add_idents_from_pattern(&loc_pattern.region, &loc_pattern.value, scope, answer);
|
||||
}
|
||||
}
|
||||
|
||||
RecordDestructure(patterns) => {
|
||||
|
|
|
@ -105,12 +105,23 @@ pub fn constrain_pattern(
|
|||
|
||||
state.constraints.push(record_con);
|
||||
}
|
||||
AppliedTag(ext_var, symbol, _arguments) => {
|
||||
AppliedTag(ext_var, symbol, patterns) => {
|
||||
let mut argument_types = Vec::with_capacity(patterns.len());
|
||||
for (pattern_var, loc_pattern) in patterns {
|
||||
state.vars.push(*pattern_var);
|
||||
|
||||
let pattern_type = Type::Variable(*pattern_var);
|
||||
argument_types.push(pattern_type.clone());
|
||||
|
||||
let expected = PExpected::NoExpectation(pattern_type);
|
||||
constrain_pattern(&loc_pattern.value, loc_pattern.region, expected, state);
|
||||
}
|
||||
|
||||
let tag_con = Constraint::Pattern(
|
||||
region,
|
||||
PatternCategory::Ctor(symbol.clone()),
|
||||
Type::TagUnion(
|
||||
vec![(symbol.clone(), vec![])],
|
||||
vec![(symbol.clone(), argument_types)],
|
||||
Box::new(Type::Variable(*ext_var)),
|
||||
),
|
||||
expected,
|
||||
|
|
|
@ -1426,7 +1426,17 @@ pub fn record_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
|||
|
||||
/// This is mainly for matching tags in closure params, e.g. \@Foo -> ...
|
||||
fn private_tag<'a>() -> impl Parser<'a, &'a str> {
|
||||
skip_first!(char('@'), global_tag())
|
||||
// TODO should be refactored so the name is not allocated again.
|
||||
map_with_arena!(
|
||||
skip_first!(char('@'), global_tag()),
|
||||
|arena: &'a Bump, name: &'a str| {
|
||||
use bumpalo::collections::string::String;
|
||||
let mut buf = String::with_capacity_in(1 + name.len(), arena);
|
||||
buf.push('@');
|
||||
buf.push_str(name);
|
||||
buf.into_bump_str()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// This is mainly for matching tags in closure params, e.g. \Foo -> ...
|
||||
|
|
|
@ -174,11 +174,22 @@ fn constrain_pattern(
|
|||
state.constraints.push(record_con);
|
||||
}
|
||||
|
||||
AppliedTag(ext_var, symbol, _arguments) => {
|
||||
AppliedTag(ext_var, symbol, patterns) => {
|
||||
let mut argument_types = Vec::with_capacity(patterns.len());
|
||||
for (pattern_var, loc_pattern) in patterns {
|
||||
state.vars.push(*pattern_var);
|
||||
|
||||
let pattern_type = Type::Variable(*pattern_var);
|
||||
argument_types.push(pattern_type.clone());
|
||||
|
||||
let expected = PExpected::NoExpectation(pattern_type);
|
||||
constrain_pattern(var_store, state, loc_pattern, expected);
|
||||
}
|
||||
|
||||
let union_type = constrain::lift(
|
||||
var_store,
|
||||
Type::TagUnion(
|
||||
vec![(symbol.clone(), vec![])],
|
||||
vec![(symbol.clone(), argument_types)],
|
||||
Box::new(Type::Variable(*ext_var)),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -1038,7 +1038,7 @@ mod test_infer {
|
|||
r#"\@Foo -> 42
|
||||
"#
|
||||
),
|
||||
"[ Test.Foo ]* -> Int",
|
||||
"[ Test.@Foo ]* -> Int",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1108,4 +1108,30 @@ mod test_infer {
|
|||
"Int",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn global_tag_with_field() {
|
||||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
when Foo 4 is
|
||||
Foo x -> x
|
||||
"#
|
||||
),
|
||||
"Int",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn private_tag_with_field() {
|
||||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
when @Foo 4 is
|
||||
@Foo x -> x
|
||||
"#
|
||||
),
|
||||
"Int",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1370,13 +1370,13 @@ mod test_parse {
|
|||
let newline = bumpalo::vec![in &arena; Newline];
|
||||
let newlines = bumpalo::vec![in &arena; Newline, Newline];
|
||||
let tag1 = Tag::Private {
|
||||
name: Located::new(0, 0, 8, 13, "True"),
|
||||
name: Located::new(0, 0, 8, 13, "@True"),
|
||||
args: &[],
|
||||
};
|
||||
let tag2arg = Located::new(0, 0, 24, 29, TypeAnnotation::Apply(&[], "Thing", &[]));
|
||||
let tag2args = bumpalo::vec![in &arena; tag2arg];
|
||||
let tag2 = Tag::Private {
|
||||
name: Located::new(0, 0, 15, 23, "Perhaps"),
|
||||
name: Located::new(0, 0, 15, 23, "@Perhaps"),
|
||||
args: tag2args.into_bump_slice(),
|
||||
};
|
||||
let tags = bumpalo::vec![in &arena;
|
||||
|
@ -1424,13 +1424,13 @@ mod test_parse {
|
|||
let newline = bumpalo::vec![in &arena; Newline];
|
||||
let newlines = bumpalo::vec![in &arena; Newline, Newline];
|
||||
let tag1 = Tag::Private {
|
||||
name: Located::new(0, 0, 8, 13, "True"),
|
||||
name: Located::new(0, 0, 8, 13, "@True"),
|
||||
args: &[],
|
||||
};
|
||||
let tag2arg = Located::new(0, 0, 24, 29, TypeAnnotation::Apply(&[], "Thing", &[]));
|
||||
let tag2args = bumpalo::vec![in &arena; tag2arg];
|
||||
let tag2 = Tag::Private {
|
||||
name: Located::new(0, 0, 15, 23, "Perhaps"),
|
||||
name: Located::new(0, 0, 15, 23, "@Perhaps"),
|
||||
args: tag2args.into_bump_slice(),
|
||||
};
|
||||
let tags = bumpalo::vec![in &arena;
|
||||
|
|
|
@ -932,7 +932,7 @@ mod test_infer_uniq {
|
|||
r#"\@Foo -> 42
|
||||
"#
|
||||
),
|
||||
"Attr.Attr * (Attr.Attr * [ Test.Foo ]* -> Attr.Attr * Int)",
|
||||
"Attr.Attr * (Attr.Attr * [ Test.@Foo ]* -> Attr.Attr * Int)",
|
||||
);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue