Parse "as" aliases into tag/args rather than arbitrary annotations

This commit is contained in:
ayazhafiz 2021-12-24 15:42:58 -06:00
parent 4ddb8e10fb
commit 2cd5bf8c03
8 changed files with 276 additions and 253 deletions

View file

@ -450,127 +450,110 @@ pub fn to_type2<'a>(
Type2::TagUnion(tag_types, ext_type) Type2::TagUnion(tag_types, ext_type)
} }
As(loc_inner, _spaces, loc_as) => { As(loc_inner, _spaces, (ident, loc_vars)) => {
// e.g. `{ x : Int, y : Int } as Point }` // e.g. `{ x : Int, y : Int } as Point`
match loc_as.value { let symbol = match scope.introduce(
Apply(module_name, ident, loc_vars) if module_name.is_empty() => { (*ident).into(),
let symbol = match scope.introduce( &env.exposed_ident_ids,
ident.into(), &mut env.ident_ids,
&env.exposed_ident_ids, region,
&mut env.ident_ids, ) {
region, Ok(symbol) => symbol,
) {
Ok(symbol) => symbol,
Err((_original_region, _shadow)) => { Err((_original_region, _shadow)) => {
// let problem = Problem2::Shadowed(original_region, shadow.clone()); // let problem = Problem2::Shadowed(original_region, shadow.clone());
// env.problem(roc_problem::can::Problem::ShadowingInAnnotation { // env.problem(roc_problem::can::Problem::ShadowingInAnnotation {
// original_region, // original_region,
// shadow, // shadow,
// }); // });
// return Type2::Erroneous(problem); // return Type2::Erroneous(problem);
todo!(); todo!();
}
};
let inner_type = to_type2(env, scope, references, &loc_inner.value, region);
let vars = PoolVec::with_capacity(loc_vars.len() as u32, env.pool);
let lowercase_vars = PoolVec::with_capacity(loc_vars.len() as u32, env.pool);
for ((loc_var, named_id), var_id) in loc_vars
.iter()
.zip(lowercase_vars.iter_node_ids())
.zip(vars.iter_node_ids())
{
match loc_var.value {
BoundVariable(ident) => {
let var_name = Lowercase::from(ident);
if let Some(var) = references.named.get(&var_name) {
let poolstr = PoolStr::new(var_name.as_str(), env.pool);
let type_id = env.pool.add(Type2::Variable(*var));
env.pool[var_id] = (poolstr.shallow_clone(), type_id);
env.pool[named_id] = (poolstr, *var);
env.set_region(named_id, loc_var.region);
} else {
let var = env.var_store.fresh();
references.named.insert(var_name.clone(), var);
let poolstr = PoolStr::new(var_name.as_str(), env.pool);
let type_id = env.pool.add(Type2::Variable(var));
env.pool[var_id] = (poolstr.shallow_clone(), type_id);
env.pool[named_id] = (poolstr, var);
env.set_region(named_id, loc_var.region);
}
}
_ => {
// If anything other than a lowercase identifier
// appears here, the whole annotation is invalid.
return Type2::Erroneous(Problem2::CanonicalizationProblem);
}
}
}
let alias_actual = inner_type;
// TODO instantiate recursive tag union
// let alias_actual = if let Type2::TagUnion(tags, ext) = inner_type {
// let rec_var = env.var_store.fresh();
//
// let mut new_tags = Vec::with_capacity(tags.len());
// for (tag_name, args) in tags {
// let mut new_args = Vec::with_capacity(args.len());
// for arg in args {
// let mut new_arg = arg.clone();
// new_arg.substitute_alias(symbol, &Type2::Variable(rec_var));
// new_args.push(new_arg);
// }
// new_tags.push((tag_name.clone(), new_args));
// }
// Type2::RecursiveTagUnion(rec_var, new_tags, ext)
// } else {
// inner_type
// };
let mut hidden_variables = MutSet::default();
hidden_variables.extend(alias_actual.variables(env.pool));
for (_, var) in lowercase_vars.iter(env.pool) {
hidden_variables.remove(var);
}
let alias_actual_id = env.pool.add(alias_actual);
scope.add_alias(env.pool, symbol, lowercase_vars, alias_actual_id);
let alias = scope.lookup_alias(symbol).unwrap();
// local_aliases.insert(symbol, alias.clone());
// TODO host-exposed
// if vars.is_empty() && env.home == symbol.module_id() {
// let actual_var = env.var_store.fresh();
// rigids.host_exposed.insert(symbol, actual_var);
// Type::HostExposedAlias {
// name: symbol,
// arguments: vars,
// actual: Box::new(alias.typ.clone()),
// actual_var,
// }
// } else {
// Type::Alias(symbol, vars, Box::new(alias.typ.clone()))
// }
Type2::AsAlias(symbol, vars, alias.actual)
} }
_ => { };
// This is a syntactically invalid type alias.
Type2::Erroneous(Problem2::CanonicalizationProblem) let inner_type = to_type2(env, scope, references, &loc_inner.value, region);
let vars = PoolVec::with_capacity(loc_vars.len() as u32, env.pool);
let lowercase_vars = PoolVec::with_capacity(loc_vars.len() as u32, env.pool);
for ((loc_var, named_id), var_id) in loc_vars
.iter()
.zip(lowercase_vars.iter_node_ids())
.zip(vars.iter_node_ids())
{
let var_name = Lowercase::from(loc_var.value);
if let Some(var) = references.named.get(&var_name) {
let poolstr = PoolStr::new(var_name.as_str(), env.pool);
let type_id = env.pool.add(Type2::Variable(*var));
env.pool[var_id] = (poolstr.shallow_clone(), type_id);
env.pool[named_id] = (poolstr, *var);
env.set_region(named_id, loc_var.region);
} else {
let var = env.var_store.fresh();
references.named.insert(var_name.clone(), var);
let poolstr = PoolStr::new(var_name.as_str(), env.pool);
let type_id = env.pool.add(Type2::Variable(var));
env.pool[var_id] = (poolstr.shallow_clone(), type_id);
env.pool[named_id] = (poolstr, var);
env.set_region(named_id, loc_var.region);
} }
} }
let alias_actual = inner_type;
// TODO instantiate recursive tag union
// let alias_actual = if let Type2::TagUnion(tags, ext) = inner_type {
// let rec_var = env.var_store.fresh();
//
// let mut new_tags = Vec::with_capacity(tags.len());
// for (tag_name, args) in tags {
// let mut new_args = Vec::with_capacity(args.len());
// for arg in args {
// let mut new_arg = arg.clone();
// new_arg.substitute_alias(symbol, &Type2::Variable(rec_var));
// new_args.push(new_arg);
// }
// new_tags.push((tag_name.clone(), new_args));
// }
// Type2::RecursiveTagUnion(rec_var, new_tags, ext)
// } else {
// inner_type
// };
let mut hidden_variables = MutSet::default();
hidden_variables.extend(alias_actual.variables(env.pool));
for (_, var) in lowercase_vars.iter(env.pool) {
hidden_variables.remove(var);
}
let alias_actual_id = env.pool.add(alias_actual);
scope.add_alias(env.pool, symbol, lowercase_vars, alias_actual_id);
let alias = scope.lookup_alias(symbol).unwrap();
// local_aliases.insert(symbol, alias.clone());
// TODO host-exposed
// if vars.is_empty() && env.home == symbol.module_id() {
// let actual_var = env.var_store.fresh();
// rigids.host_exposed.insert(symbol, actual_var);
// Type::HostExposedAlias {
// name: symbol,
// arguments: vars,
// actual: Box::new(alias.typ.clone()),
// actual_var,
// }
// } else {
// Type::Alias(symbol, vars, Box::new(alias.typ.clone()))
// }
Type2::AsAlias(symbol, vars, alias.actual)
} }
SpaceBefore(nested, _) | SpaceAfter(nested, _) => { SpaceBefore(nested, _) | SpaceAfter(nested, _) => {
to_type2(env, scope, references, nested, region) to_type2(env, scope, references, nested, region)

View file

@ -576,11 +576,9 @@ impl<'a> RemoveSpaces<'a> for TypeAnnotation<'a> {
), ),
TypeAnnotation::Apply(a, b, c) => TypeAnnotation::Apply(a, b, c.remove_spaces(arena)), TypeAnnotation::Apply(a, b, c) => TypeAnnotation::Apply(a, b, c.remove_spaces(arena)),
TypeAnnotation::BoundVariable(a) => TypeAnnotation::BoundVariable(a), TypeAnnotation::BoundVariable(a) => TypeAnnotation::BoundVariable(a),
TypeAnnotation::As(a, _, c) => TypeAnnotation::As( TypeAnnotation::As(a, _, c) => {
arena.alloc(a.remove_spaces(arena)), TypeAnnotation::As(arena.alloc(a.remove_spaces(arena)), &[], c)
&[], }
arena.alloc(c.remove_spaces(arena)),
),
TypeAnnotation::Record { fields, ext } => TypeAnnotation::Record { TypeAnnotation::Record { fields, ext } => TypeAnnotation::Record {
fields: fields.remove_spaces(arena), fields: fields.remove_spaces(arena),
ext: ext.remove_spaces(arena), ext: ext.remove_spaces(arena),

View file

@ -374,124 +374,109 @@ fn can_annotation_help(
} }
} }
} }
As(loc_inner, _spaces, loc_as) => match loc_as.value { As(loc_inner, _spaces, (ident, loc_vars)) => {
TypeAnnotation::Apply(module_name, ident, loc_vars) if module_name.is_empty() => { let symbol = match scope.introduce(
let symbol = match scope.introduce( (*ident).into(),
ident.into(), &env.exposed_ident_ids,
&env.exposed_ident_ids, &mut env.ident_ids,
&mut env.ident_ids, region,
region, ) {
) { Ok(symbol) => symbol,
Ok(symbol) => symbol,
Err((original_region, shadow)) => { Err((original_region, shadow)) => {
let problem = Problem::Shadowed(original_region, shadow.clone()); let problem = Problem::Shadowed(original_region, shadow.clone());
env.problem(roc_problem::can::Problem::ShadowingInAnnotation { env.problem(roc_problem::can::Problem::ShadowingInAnnotation {
original_region, original_region,
shadow, shadow,
}); });
return Type::Erroneous(problem); return Type::Erroneous(problem);
}
};
let inner_type = can_annotation_help(
env,
&loc_inner.value,
region,
scope,
var_store,
introduced_variables,
local_aliases,
references,
);
let mut vars = Vec::with_capacity(loc_vars.len());
let mut lowercase_vars = Vec::with_capacity(loc_vars.len());
references.insert(symbol);
for loc_var in loc_vars {
match loc_var.value {
BoundVariable(ident) => {
let var_name = Lowercase::from(ident);
if let Some(var) = introduced_variables.var_by_name(&var_name) {
vars.push((var_name.clone(), Type::Variable(*var)));
lowercase_vars.push(Loc::at(loc_var.region, (var_name, *var)));
} else {
let var = var_store.fresh();
introduced_variables.insert_named(var_name.clone(), var);
vars.push((var_name.clone(), Type::Variable(var)));
lowercase_vars.push(Loc::at(loc_var.region, (var_name, var)));
}
}
_ => {
// If anything other than a lowercase identifier
// appears here, the whole annotation is invalid.
return Type::Erroneous(Problem::CanonicalizationProblem);
}
}
} }
};
let alias_actual = if let Type::TagUnion(tags, ext) = inner_type { let inner_type = can_annotation_help(
let rec_var = var_store.fresh(); env,
&loc_inner.value,
region,
scope,
var_store,
introduced_variables,
local_aliases,
references,
);
let mut vars = Vec::with_capacity(loc_vars.len());
let mut lowercase_vars = Vec::with_capacity(loc_vars.len());
let mut new_tags = Vec::with_capacity(tags.len()); references.insert(symbol);
for (tag_name, args) in tags {
let mut new_args = Vec::with_capacity(args.len()); for loc_var in *loc_vars {
for arg in args { let var_name = Lowercase::from(loc_var.value);
let mut new_arg = arg.clone();
new_arg.substitute_alias(symbol, &Type::Variable(rec_var)); if let Some(var) = introduced_variables.var_by_name(&var_name) {
new_args.push(new_arg); vars.push((var_name.clone(), Type::Variable(*var)));
} lowercase_vars.push(Loc::at(loc_var.region, (var_name, *var)));
new_tags.push((tag_name.clone(), new_args));
}
Type::RecursiveTagUnion(rec_var, new_tags, ext)
} else { } else {
inner_type let var = var_store.fresh();
};
let mut hidden_variables = MutSet::default(); introduced_variables.insert_named(var_name.clone(), var);
hidden_variables.extend(alias_actual.variables()); vars.push((var_name.clone(), Type::Variable(var)));
for loc_var in lowercase_vars.iter() { lowercase_vars.push(Loc::at(loc_var.region, (var_name, var)));
hidden_variables.remove(&loc_var.value.1);
}
scope.add_alias(symbol, region, lowercase_vars, alias_actual);
let alias = scope.lookup_alias(symbol).unwrap();
local_aliases.insert(symbol, alias.clone());
// Type::Alias(symbol, vars, Box::new(alias.typ.clone()))
if vars.is_empty() && env.home == symbol.module_id() {
let actual_var = var_store.fresh();
introduced_variables.insert_host_exposed_alias(symbol, actual_var);
Type::HostExposedAlias {
name: symbol,
type_arguments: vars,
lambda_set_variables: alias.lambda_set_variables.clone(),
actual: Box::new(alias.typ.clone()),
actual_var,
}
} else {
Type::Alias {
symbol,
type_arguments: vars,
lambda_set_variables: alias.lambda_set_variables.clone(),
actual: Box::new(alias.typ.clone()),
}
} }
} }
_ => {
// This is a syntactically invalid type alias. let alias_actual = if let Type::TagUnion(tags, ext) = inner_type {
Type::Erroneous(Problem::CanonicalizationProblem) let rec_var = var_store.fresh();
let mut new_tags = Vec::with_capacity(tags.len());
for (tag_name, args) in tags {
let mut new_args = Vec::with_capacity(args.len());
for arg in args {
let mut new_arg = arg.clone();
new_arg.substitute_alias(symbol, &Type::Variable(rec_var));
new_args.push(new_arg);
}
new_tags.push((tag_name.clone(), new_args));
}
Type::RecursiveTagUnion(rec_var, new_tags, ext)
} else {
inner_type
};
let mut hidden_variables = MutSet::default();
hidden_variables.extend(alias_actual.variables());
for loc_var in lowercase_vars.iter() {
hidden_variables.remove(&loc_var.value.1);
} }
},
scope.add_alias(symbol, region, lowercase_vars, alias_actual);
let alias = scope.lookup_alias(symbol).unwrap();
local_aliases.insert(symbol, alias.clone());
// Type::Alias(symbol, vars, Box::new(alias.typ.clone()))
if vars.is_empty() && env.home == symbol.module_id() {
let actual_var = var_store.fresh();
introduced_variables.insert_host_exposed_alias(symbol, actual_var);
Type::HostExposedAlias {
name: symbol,
type_arguments: vars,
lambda_set_variables: alias.lambda_set_variables.clone(),
actual: Box::new(alias.typ.clone()),
actual_var,
}
} else {
Type::Alias {
symbol,
type_arguments: vars,
lambda_set_variables: alias.lambda_set_variables.clone(),
actual: Box::new(alias.typ.clone()),
}
}
}
Record { fields, ext } => { Record { fields, ext } => {
let ext_type = match ext { let ext_type = match ext {

View file

@ -126,7 +126,7 @@ impl<'a> Formattable for TypeAnnotation<'a> {
|| args.iter().any(|loc_arg| (&loc_arg.value).is_multiline()) || args.iter().any(|loc_arg| (&loc_arg.value).is_multiline())
} }
Apply(_, _, args) => args.iter().any(|loc_arg| loc_arg.value.is_multiline()), Apply(_, _, args) => args.iter().any(|loc_arg| loc_arg.value.is_multiline()),
As(lhs, _, rhs) => lhs.value.is_multiline() || rhs.value.is_multiline(), As(lhs, _, _) => lhs.value.is_multiline(),
Record { fields, ext } => { Record { fields, ext } => {
match ext { match ext {
@ -245,12 +245,17 @@ impl<'a> Formattable for TypeAnnotation<'a> {
} }
} }
As(lhs, _spaces, rhs) => { As(lhs, _spaces, (name, args)) => {
// TODO use spaces? // TODO use spaces?
lhs.value.format(buf, indent); lhs.value.format(buf, indent);
buf.push_str(" as");
buf.spaces(1); buf.spaces(1);
rhs.value.format(buf, indent); buf.push_str("as");
buf.spaces(1);
buf.push_str(name);
for arg in *args {
buf.spaces(1);
buf.push_str(arg.value);
}
} }
SpaceBefore(ann, spaces) => { SpaceBefore(ann, spaces) => {

View file

@ -280,7 +280,7 @@ pub enum TypeAnnotation<'a> {
As( As(
&'a Loc<TypeAnnotation<'a>>, &'a Loc<TypeAnnotation<'a>>,
&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>],
&'a Loc<TypeAnnotation<'a>>, (&'a str, &'a [Loc<&'a str>]),
), ),
Record { Record {

View file

@ -482,6 +482,7 @@ pub enum EType<'a> {
TTagUnion(ETypeTagUnion<'a>, Position), TTagUnion(ETypeTagUnion<'a>, Position),
TInParens(ETypeInParens<'a>, Position), TInParens(ETypeInParens<'a>, Position),
TApply(ETypeApply, Position), TApply(ETypeApply, Position),
TInlineAlias(ETypeInlineAlias, Position),
TBadTypeVariable(Position), TBadTypeVariable(Position),
TWildcard(Position), TWildcard(Position),
TInferred(Position), TInferred(Position),
@ -553,6 +554,13 @@ pub enum ETypeApply {
StartIsNumber(Position), StartIsNumber(Position),
} }
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ETypeInlineAlias {
NotAnAlias(Position),
Qualified(Position),
ArgumentNotLowercase(Position),
}
#[derive(Debug)] #[derive(Debug)]
pub struct ParseProblem<'a, T> { pub struct ParseProblem<'a, T> {
pub pos: Position, pub pos: Position,

View file

@ -3,13 +3,13 @@ use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
use crate::keyword; use crate::keyword;
use crate::parser::{ use crate::parser::{
allocated, backtrackable, optional, specialize, specialize_ref, word1, word2, EType, allocated, backtrackable, optional, specialize, specialize_ref, word1, word2, EType,
ETypeApply, ETypeInParens, ETypeRecord, ETypeTagUnion, ParseResult, Parser, ETypeApply, ETypeInParens, ETypeInlineAlias, ETypeRecord, ETypeTagUnion, ParseResult, Parser,
Progress::{self, *}, Progress::{self, *},
}; };
use crate::state::State; use crate::state::State;
use bumpalo::collections::vec::Vec; use bumpalo::collections::vec::Vec;
use bumpalo::Bump; use bumpalo::Bump;
use roc_region::all::{Loc, Position, Region}; use roc_region::all::{Loc, Position};
pub fn located_help<'a>( pub fn located_help<'a>(
min_indent: u16, min_indent: u16,
@ -47,6 +47,56 @@ fn tag_union_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>, ET
} }
} }
fn check_type_alias<'a>(
p: Progress,
annot: Loc<TypeAnnotation<'a>>,
) -> impl Parser<'a, Loc<(&'a str, &'a [Loc<&'a str>])>, ETypeInlineAlias> {
move |arena, state| match annot.value {
TypeAnnotation::Apply("", tag_name, args) => {
let mut arg_names = Vec::new_in(arena);
arg_names.reserve(args.len());
for arg in args {
if let TypeAnnotation::BoundVariable(v) = arg.value {
arg_names.push(Loc::at(arg.region, v));
} else {
return Err((
p,
ETypeInlineAlias::ArgumentNotLowercase(arg.region.start()),
state,
));
}
}
Ok((
p,
Loc::at(annot.region, (tag_name, arg_names.into_bump_slice())),
state,
))
}
TypeAnnotation::Apply(_, _, _) => {
Err((p, ETypeInlineAlias::Qualified(annot.region.start()), state))
}
_ => Err((p, ETypeInlineAlias::NotAnAlias(annot.region.start()), state)),
}
}
fn parse_type_alias_after_as<'a>(
min_indent: u16,
) -> impl Parser<'a, Loc<(&'a str, &'a [Loc<&'a str>])>, EType<'a>> {
move |arena, state| {
space0_before_e(
term(min_indent),
min_indent,
EType::TSpace,
EType::TAsIndentStart,
)
.parse(arena, state)
.and_then(|(p, annot, state)| {
specialize(EType::TInlineAlias, check_type_alias(p, annot)).parse(arena, state)
})
}
}
fn fail_type_start<'a, T: 'a>() -> impl Parser<'a, T, EType<'a>> { fn fail_type_start<'a, T: 'a>() -> impl Parser<'a, T, EType<'a>> {
|_arena, state: State<'a>| Err((NoProgress, EType::TStart(state.pos), state)) |_arena, state: State<'a>| Err((NoProgress, EType::TStart(state.pos), state))
} }
@ -72,12 +122,7 @@ fn term<'a>(min_indent: u16) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'
backtrackable(space0_e(min_indent, EType::TSpace, EType::TIndentEnd)), backtrackable(space0_e(min_indent, EType::TSpace, EType::TIndentEnd)),
crate::parser::keyword_e(keyword::AS, EType::TEnd) crate::parser::keyword_e(keyword::AS, EType::TEnd)
), ),
space0_before_e( parse_type_alias_after_as(min_indent)
term(min_indent),
min_indent,
EType::TSpace,
EType::TAsIndentStart
)
), ),
Some Some
), ),
@ -87,13 +132,17 @@ fn term<'a>(min_indent: u16) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'
|arena: &'a Bump, |arena: &'a Bump,
(loc_ann, opt_as): ( (loc_ann, opt_as): (
Loc<TypeAnnotation<'a>>, Loc<TypeAnnotation<'a>>,
Option<(&'a [_], Loc<TypeAnnotation<'a>>)> Option<(&'a [_], Loc<(&'a str, &'a [Loc<&'a str>])>)>
)| { )| {
match opt_as { match opt_as {
Some((spaces, loc_as)) => { Some((
let region = Region::span_across(&loc_ann.region, &loc_as.region); spaces,
let value = Loc {
TypeAnnotation::As(arena.alloc(loc_ann), spaces, arena.alloc(loc_as)); region,
value: alias,
},
)) => {
let value = TypeAnnotation::As(arena.alloc(loc_ann), spaces, alias);
Loc { region, value } Loc { region, value }
} }

View file

@ -4,7 +4,7 @@ Defs(
|L 0-0, C 0-3| Identifier( |L 0-0, C 0-3| Identifier(
"foo", "foo",
), ),
|L 0-0, C 6-33| As( |L 0-0, C 25-33| As(
|L 0-0, C 6-21| Apply( |L 0-0, C 6-21| Apply(
"Foo.Bar", "Foo.Bar",
"Baz", "Baz",
@ -18,16 +18,11 @@ Defs(
], ],
), ),
[], [],
|L 0-0, C 25-33| Apply( (
"",
"Blah", "Blah",
[ [
|L 0-0, C 30-31| BoundVariable( |L 0-0, C 30-31| "a",
"a", |L 0-0, C 32-33| "b",
),
|L 0-0, C 32-33| BoundVariable(
"b",
),
], ],
), ),
), ),