Implement open-in-output-position for non-alias type annotations

This commit is contained in:
Ayaz Hafiz 2022-10-25 18:00:50 -05:00
parent 0707739cfc
commit 8d5678151d
No known key found for this signature in database
GPG Key ID: 0E2A37416A25EF58
3 changed files with 82 additions and 6 deletions

View File

@ -260,14 +260,18 @@ fn malformed(env: &mut Env, region: Region, name: &str) {
env.problem(roc_problem::can::Problem::RuntimeError(problem));
}
/// Is an annotation on a value, or a value prototype (ability member)?
pub(crate) struct ValueAnnotation(pub bool);
/// Canonicalizes a top-level type annotation.
pub fn canonicalize_annotation(
pub(crate) fn canonicalize_annotation(
env: &mut Env,
scope: &mut Scope,
annotation: &TypeAnnotation,
region: Region,
var_store: &mut VarStore,
pending_abilities_in_scope: &PendingAbilitiesInScope,
value_annotation: ValueAnnotation,
) -> Annotation {
let mut introduced_variables = IntroducedVariables::default();
let mut references = VecSet::default();
@ -301,8 +305,18 @@ pub fn canonicalize_annotation(
annot => (annot, region),
};
let pol = if value_annotation.0 {
// Values always have positive polarity.
CanPolarity::Pos
} else {
// This is an annotation for an alias or opaque type; the polarity can only be determined
// contextually.
CanPolarity::Disregard
};
let typ = can_annotation_help(
env,
pol,
annotation,
region,
scope,
@ -320,6 +334,30 @@ pub fn canonicalize_annotation(
}
}
#[derive(Clone, Copy)]
enum CanPolarity {
/// Polarity should be disregarded for now; relevant in aliaes + opaques.
Disregard,
Neg,
Pos,
}
impl CanPolarity {
fn set_neg(self) -> Self {
match self {
CanPolarity::Disregard => CanPolarity::Disregard,
CanPolarity::Neg | CanPolarity::Pos => CanPolarity::Neg,
}
}
fn set_pos(self) -> Self {
match self {
CanPolarity::Disregard => CanPolarity::Disregard,
CanPolarity::Neg | CanPolarity::Pos => CanPolarity::Pos,
}
}
}
pub(crate) fn make_apply_symbol(
env: &mut Env,
region: Region,
@ -472,6 +510,7 @@ fn find_fresh_var_name(introduced_variables: &IntroducedVariables) -> Lowercase
#[allow(clippy::too_many_arguments)]
fn can_annotation_help(
env: &mut Env,
pol: CanPolarity,
annotation: &roc_parse::ast::TypeAnnotation,
region: Region,
scope: &mut Scope,
@ -489,6 +528,7 @@ fn can_annotation_help(
for arg in *argument_types {
let arg_ann = can_annotation_help(
env,
pol.set_neg(),
&arg.value,
arg.region,
scope,
@ -503,6 +543,7 @@ fn can_annotation_help(
let ret = can_annotation_help(
env,
pol.set_pos(),
&return_type.value,
return_type.region,
scope,
@ -550,6 +591,7 @@ fn can_annotation_help(
for arg in *type_arguments {
let arg_ann = can_annotation_help(
env,
pol,
&arg.value,
arg.region,
scope,
@ -649,6 +691,7 @@ fn can_annotation_help(
let inner_type = can_annotation_help(
env,
CanPolarity::Disregard,
&loc_inner.value,
region,
scope,
@ -783,6 +826,7 @@ fn can_annotation_help(
Record { fields, ext } => {
let ext_type = can_extension_type(
env,
pol,
scope,
var_store,
introduced_variables,
@ -806,6 +850,7 @@ fn can_annotation_help(
} else {
let field_types = can_assigned_fields(
env,
pol,
&fields.items,
region,
scope,
@ -821,6 +866,7 @@ fn can_annotation_help(
TagUnion { tags, ext, .. } => {
let ext_type = can_extension_type(
env,
pol,
scope,
var_store,
introduced_variables,
@ -844,6 +890,7 @@ fn can_annotation_help(
} else {
let mut tag_types = can_tags(
env,
pol,
tags.items,
region,
scope,
@ -863,6 +910,7 @@ fn can_annotation_help(
}
SpaceBefore(nested, _) | SpaceAfter(nested, _) => can_annotation_help(
env,
pol,
nested,
region,
scope,
@ -989,6 +1037,7 @@ fn canonicalize_has_clause(
#[allow(clippy::too_many_arguments)]
fn can_extension_type<'a>(
env: &mut Env,
pol: CanPolarity,
scope: &mut Scope,
var_store: &mut VarStore,
introduced_variables: &mut IntroducedVariables,
@ -1013,15 +1062,16 @@ fn can_extension_type<'a>(
use roc_problem::can::ExtensionTypeKind;
let (empty_ext_type, valid_extension_type): (_, fn(&Type) -> bool) = match ext_problem_kind {
ExtensionTypeKind::Record => (Type::EmptyRec, valid_record_ext_type),
ExtensionTypeKind::TagUnion => (Type::EmptyTagUnion, valid_tag_ext_type),
let valid_extension_type: fn(&Type) -> bool = match ext_problem_kind {
ExtensionTypeKind::Record => valid_record_ext_type,
ExtensionTypeKind::TagUnion => valid_tag_ext_type,
};
match opt_ext {
Some(loc_ann) => {
let ext_type = can_annotation_help(
env,
pol,
&loc_ann.value,
loc_ann.region,
scope,
@ -1050,7 +1100,22 @@ fn can_extension_type<'a>(
Type::Variable(var)
}
}
None => empty_ext_type,
None => match ext_problem_kind {
ExtensionTypeKind::Record => Type::EmptyRec,
ExtensionTypeKind::TagUnion => {
// In negative positions a missing extension variable forces a closed tag union;
// otherwise, open-in-output-position means we give the tag an inference variable.
match pol {
CanPolarity::Neg | CanPolarity::Disregard => Type::EmptyTagUnion,
CanPolarity::Pos => {
let var = var_store.fresh();
introduced_variables.insert_inferred(Loc::at_zero(var));
Type::Variable(var)
}
}
}
},
}
}
@ -1180,6 +1245,7 @@ where
#[allow(clippy::too_many_arguments)]
fn can_assigned_fields<'a>(
env: &mut Env,
pol: CanPolarity,
fields: &&[Loc<AssignedField<'a, TypeAnnotation<'a>>>],
region: Region,
scope: &mut Scope,
@ -1209,6 +1275,7 @@ fn can_assigned_fields<'a>(
RequiredValue(field_name, _, annotation) => {
let field_type = can_annotation_help(
env,
pol,
&annotation.value,
annotation.region,
scope,
@ -1226,6 +1293,7 @@ fn can_assigned_fields<'a>(
OptionalValue(field_name, _, annotation) => {
let field_type = can_annotation_help(
env,
pol,
&annotation.value,
annotation.region,
scope,
@ -1293,6 +1361,7 @@ fn can_assigned_fields<'a>(
#[allow(clippy::too_many_arguments)]
fn can_tags<'a>(
env: &mut Env,
pol: CanPolarity,
tags: &'a [Loc<Tag<'a>>],
region: Region,
scope: &mut Scope,
@ -1322,6 +1391,7 @@ fn can_tags<'a>(
for arg in args.iter() {
let ann = can_annotation_help(
env,
pol,
&arg.value,
arg.region,
scope,

View File

@ -7,6 +7,7 @@ use crate::annotation::find_type_def_symbols;
use crate::annotation::make_apply_symbol;
use crate::annotation::IntroducedVariables;
use crate::annotation::OwnedNamedOrAble;
use crate::annotation::ValueAnnotation;
use crate::derive;
use crate::env::Env;
use crate::expr::AccessorData;
@ -327,6 +328,7 @@ fn canonicalize_alias<'a>(
ann.region,
var_store,
pending_abilities_in_scope,
ValueAnnotation(false),
);
// Record all the annotation's references in output.references.lookups
@ -1329,6 +1331,7 @@ fn resolve_abilities<'a>(
typ.region,
var_store,
pending_abilities_in_scope,
ValueAnnotation(true),
);
// Record all the annotation's references in output.references.lookups
@ -1996,6 +1999,7 @@ fn canonicalize_pending_value_def<'a>(
loc_ann.region,
var_store,
pending_abilities_in_scope,
ValueAnnotation(true),
);
// Record all the annotation's references in output.references.lookups
@ -2095,6 +2099,7 @@ fn canonicalize_pending_value_def<'a>(
loc_ann.region,
var_store,
pending_abilities_in_scope,
ValueAnnotation(true),
);
// Record all the annotation's references in output.references.lookups

View File

@ -1,5 +1,5 @@
use crate::abilities::{AbilitiesStore, ImplKey, PendingAbilitiesStore, ResolvedImpl};
use crate::annotation::canonicalize_annotation;
use crate::annotation::{canonicalize_annotation, ValueAnnotation};
use crate::def::{canonicalize_defs, Def};
use crate::effect_module::HostedGeneratedFunctions;
use crate::env::Env;
@ -442,6 +442,7 @@ pub fn canonicalize_module_defs<'a>(
loc_ann.region,
var_store,
pending_abilities_in_scope,
ValueAnnotation(true),
);
ann.add_to(