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:
Folkert 2020-01-09 13:20:21 +01:00
parent 98515cdf23
commit dd240cf53a
7 changed files with 104 additions and 29 deletions

View file

@ -17,7 +17,7 @@ use im_rc::Vector;
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Pattern { pub enum Pattern {
Identifier(Symbol), Identifier(Symbol),
AppliedTag(Variable, Symbol, Vec<Located<Pattern>>), AppliedTag(Variable, Symbol, Vec<(Variable, Located<Pattern>)>),
IntLiteral(i64), IntLiteral(i64),
FloatLiteral(f64), FloatLiteral(f64),
StrLiteral(Box<str>), StrLiteral(Box<str>),
@ -94,6 +94,32 @@ pub fn canonicalize_pattern<'a>(
vec![], 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 { &FloatLiteral(ref string) => match pattern_type {
WhenBranch => { WhenBranch => {
let float = finish_parsing_float(string) let float = finish_parsing_float(string)
@ -344,13 +370,10 @@ pub fn remove_idents(pattern: &ast::Pattern, idents: &mut ImMap<Ident, (Symbol,
QualifiedIdentifier(_name) => { QualifiedIdentifier(_name) => {
panic!("TODO implement QualifiedIdentifier pattern in remove_idents."); panic!("TODO implement QualifiedIdentifier pattern in remove_idents.");
} }
Apply(_, _) => { Apply(_, patterns) => {
panic!("TODO implement Apply pattern in remove_idents."); for loc_pattern in *patterns {
// AppliedVariant(_, Some(loc_args)) => { remove_idents(&loc_pattern.value, idents);
// for loc_arg in loc_args { }
// remove_idents(loc_arg.value, idents);
// }
// }
} }
RecordDestructure(patterns) => { RecordDestructure(patterns) => {
for loc_pattern in patterns { for loc_pattern in patterns {
@ -410,16 +433,10 @@ fn add_idents_from_pattern<'a>(
QualifiedIdentifier(_name) => { QualifiedIdentifier(_name) => {
panic!("TODO implement QualifiedIdentifier pattern."); panic!("TODO implement QualifiedIdentifier pattern.");
} }
Apply(_, _) => { Apply(_tag, patterns) => {
panic!("TODO implement Apply pattern."); for loc_pattern in *patterns {
// &AppliedVariant(_, ref opt_loc_args) => match opt_loc_args { add_idents_from_pattern(&loc_pattern.region, &loc_pattern.value, scope, answer);
// &None => (), }
// &Some(ref loc_args) => {
// for loc_arg in loc_args.iter() {
// add_idents_from_pattern(loc_arg, scope, answer);
// }
// }
// },
} }
RecordDestructure(patterns) => { RecordDestructure(patterns) => {

View file

@ -105,12 +105,23 @@ pub fn constrain_pattern(
state.constraints.push(record_con); 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( let tag_con = Constraint::Pattern(
region, region,
PatternCategory::Ctor(symbol.clone()), PatternCategory::Ctor(symbol.clone()),
Type::TagUnion( Type::TagUnion(
vec![(symbol.clone(), vec![])], vec![(symbol.clone(), argument_types)],
Box::new(Type::Variable(*ext_var)), Box::new(Type::Variable(*ext_var)),
), ),
expected, expected,

View file

@ -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 -> ... /// This is mainly for matching tags in closure params, e.g. \@Foo -> ...
fn private_tag<'a>() -> impl Parser<'a, &'a str> { 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 -> ... /// This is mainly for matching tags in closure params, e.g. \Foo -> ...

View file

@ -174,11 +174,22 @@ fn constrain_pattern(
state.constraints.push(record_con); 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( let union_type = constrain::lift(
var_store, var_store,
Type::TagUnion( Type::TagUnion(
vec![(symbol.clone(), vec![])], vec![(symbol.clone(), argument_types)],
Box::new(Type::Variable(*ext_var)), Box::new(Type::Variable(*ext_var)),
), ),
); );

View file

@ -1038,7 +1038,7 @@ mod test_infer {
r#"\@Foo -> 42 r#"\@Foo -> 42
"# "#
), ),
"[ Test.Foo ]* -> Int", "[ Test.@Foo ]* -> Int",
); );
} }
@ -1108,4 +1108,30 @@ mod test_infer {
"Int", "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",
);
}
} }

View file

@ -1370,13 +1370,13 @@ mod test_parse {
let newline = bumpalo::vec![in &arena; Newline]; let newline = bumpalo::vec![in &arena; Newline];
let newlines = bumpalo::vec![in &arena; Newline, Newline]; let newlines = bumpalo::vec![in &arena; Newline, Newline];
let tag1 = Tag::Private { let tag1 = Tag::Private {
name: Located::new(0, 0, 8, 13, "True"), name: Located::new(0, 0, 8, 13, "@True"),
args: &[], args: &[],
}; };
let tag2arg = Located::new(0, 0, 24, 29, TypeAnnotation::Apply(&[], "Thing", &[])); let tag2arg = Located::new(0, 0, 24, 29, TypeAnnotation::Apply(&[], "Thing", &[]));
let tag2args = bumpalo::vec![in &arena; tag2arg]; let tag2args = bumpalo::vec![in &arena; tag2arg];
let tag2 = Tag::Private { 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(), args: tag2args.into_bump_slice(),
}; };
let tags = bumpalo::vec![in &arena; let tags = bumpalo::vec![in &arena;
@ -1424,13 +1424,13 @@ mod test_parse {
let newline = bumpalo::vec![in &arena; Newline]; let newline = bumpalo::vec![in &arena; Newline];
let newlines = bumpalo::vec![in &arena; Newline, Newline]; let newlines = bumpalo::vec![in &arena; Newline, Newline];
let tag1 = Tag::Private { let tag1 = Tag::Private {
name: Located::new(0, 0, 8, 13, "True"), name: Located::new(0, 0, 8, 13, "@True"),
args: &[], args: &[],
}; };
let tag2arg = Located::new(0, 0, 24, 29, TypeAnnotation::Apply(&[], "Thing", &[])); let tag2arg = Located::new(0, 0, 24, 29, TypeAnnotation::Apply(&[], "Thing", &[]));
let tag2args = bumpalo::vec![in &arena; tag2arg]; let tag2args = bumpalo::vec![in &arena; tag2arg];
let tag2 = Tag::Private { 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(), args: tag2args.into_bump_slice(),
}; };
let tags = bumpalo::vec![in &arena; let tags = bumpalo::vec![in &arena;

View file

@ -932,7 +932,7 @@ mod test_infer_uniq {
r#"\@Foo -> 42 r#"\@Foo -> 42
"# "#
), ),
"Attr.Attr * (Attr.Attr * [ Test.Foo ]* -> Attr.Attr * Int)", "Attr.Attr * (Attr.Attr * [ Test.@Foo ]* -> Attr.Attr * Int)",
); );
} }