Refactor headers_from_annotation to match over TypeTag

This commit is contained in:
Ayaz Hafiz 2023-04-10 15:44:11 -05:00
parent c8de1e5a27
commit 7fa508b3c8
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58

View file

@ -5,14 +5,15 @@ use roc_can::expected::{Expected, PExpected};
use roc_can::pattern::Pattern::{self, *};
use roc_can::pattern::{DestructType, ListPatterns, RecordDestruct, TupleDestruct};
use roc_collections::all::{HumanIndex, SendMap};
use roc_collections::soa::Index;
use roc_collections::VecMap;
use roc_module::ident::Lowercase;
use roc_module::symbol::Symbol;
use roc_region::all::{Loc, Region};
use roc_types::subs::Variable;
use roc_types::types::{
AliasKind, Category, OptAbleType, PReason, PatternCategory, Reason, RecordField, Type,
TypeExtension, TypeTag, Types,
AliasKind, AliasShared, Category, OptAbleType, PReason, PatternCategory, Reason, RecordField,
Type, TypeExtension, TypeTag, Types,
};
#[derive(Default, Debug)]
@ -31,10 +32,10 @@ pub struct PatternState {
/// Would add `x => <42>` to the headers (i.e., symbol points to a type variable). If the
/// definition has an annotation, we instead now add `x => Int`.
pub fn headers_from_annotation(
types: &mut Types,
types: &Types,
constraints: &mut Constraints,
pattern: &Pattern,
annotation: &Loc<&Type>,
annotation: &Loc<Index<TypeTag>>,
) -> Option<VecMap<Symbol, Loc<TypeOrVar>>> {
let mut headers = VecMap::default();
// Check that the annotation structurally agrees with the pattern, preventing e.g. `{ x, y } : Int`
@ -51,12 +52,13 @@ pub fn headers_from_annotation(
}
fn headers_from_annotation_help(
types: &mut Types,
types: &Types,
constraints: &mut Constraints,
pattern: &Pattern,
annotation: &Loc<&Type>,
annotation: &Loc<Index<TypeTag>>,
headers: &mut VecMap<Symbol, Loc<TypeOrVar>>,
) -> bool {
let typ = annotation.value;
match pattern {
Identifier(symbol)
| Shadowed(_, _, symbol)
@ -64,20 +66,14 @@ fn headers_from_annotation_help(
ident: symbol,
specializes: _,
} => {
let annotation_index = {
let typ = types.from_old_type(annotation.value);
constraints.push_type(types, typ)
};
let annotation_index = 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 annotation_index = constraints.push_type(types, typ);
let typ = Loc::at(annotation.region, annotation_index);
headers.insert(*symbol, typ);
@ -94,36 +90,45 @@ fn headers_from_annotation_help(
| SingleQuote(..)
| StrLiteral(_) => true,
RecordDestructure { destructs, .. } => match annotation.value.shallow_dealias() {
Type::Record(fields, _) => {
for loc_destruct in destructs {
let destruct = &loc_destruct.value;
RecordDestructure { destructs, .. } => {
let dealiased = types.shallow_dealias(annotation.value);
match types[dealiased] {
TypeTag::Record(fields) => {
let (field_names, _, field_types) = types.record_fields_slices(fields);
let field_names = &types[field_names];
// NOTE: We ignore both Guard and optionality when
// determining the type of the assigned def (which is what
// gets added to the header here).
//
// For example, no matter whether it's `{ x } = rec` or
// `{ 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)
};
headers.insert(
destruct.symbol,
Loc::at(annotation.region, field_type_index),
);
} else {
return false;
for loc_destruct in destructs {
let destruct = &loc_destruct.value;
// NOTE: We ignore both Guard and optionality when
// determining the type of the assigned def (which is what
// gets added to the header here).
//
// For example, no matter whether it's `{ x } = rec` or
// `{ x ? 0 } = rec` or `{ x: 5 } -> ...` in all cases
// the type of `x` within the binding itself is the same.
if let Some(i) = field_names
.iter()
.position(|field| field == &destruct.label)
{
let field_type_index = {
let typ = field_types.at(i);
constraints.push_type(types, typ)
};
headers.insert(
destruct.symbol,
Loc::at(annotation.region, field_type_index),
);
} else {
return false;
}
}
true
}
true
TypeTag::EmptyRecord => destructs.is_empty(),
_ => false,
}
Type::EmptyRec => destructs.is_empty(),
_ => false,
},
}
TupleDestructure { destructs: _, .. } => {
todo!();
@ -132,7 +137,7 @@ fn headers_from_annotation_help(
List { patterns, .. } => {
if let Some((_, Some(rest))) = patterns.opt_rest {
let annotation_index = {
let typ = types.from_old_type(annotation.value);
let typ = annotation.value;
constraints.push_type(types, typ)
};
let typ = Loc::at(annotation.region, annotation_index);
@ -152,31 +157,38 @@ fn headers_from_annotation_help(
tag_name,
arguments,
..
} => match annotation.value.shallow_dealias() {
Type::TagUnion(tags, _) => {
if let Some((_, arg_types)) = tags.iter().find(|(name, _)| name == tag_name) {
if !arguments.len() == arg_types.len() {
return false;
}
} => {
let dealiased = types.shallow_dealias(annotation.value);
match types[dealiased] {
TypeTag::TagUnion(tags, _) => {
let (tags, payloads) = types.union_tag_slices(tags);
let tags = &types[tags];
arguments
.iter()
.zip(arg_types.iter())
.all(|(arg_pattern, arg_type)| {
headers_from_annotation_help(
types,
constraints,
&arg_pattern.1.value,
&Loc::at(annotation.region, arg_type),
headers,
)
})
} else {
false
if let Some(i) = tags.iter().position(|name| name == tag_name) {
let arg_types_slice = types[payloads.at(i)];
if !arguments.len() == arg_types_slice.len() {
return false;
}
arguments.iter().zip(arg_types_slice.into_iter()).all(
|(arg_pattern, arg_type)| {
headers_from_annotation_help(
types,
constraints,
&arg_pattern.1.value,
&Loc::at(annotation.region, arg_type),
headers,
)
},
)
} else {
false
}
}
_ => false,
}
_ => false,
},
}
UnwrappedOpaque {
whole_var: _,
@ -185,36 +197,41 @@ fn headers_from_annotation_help(
specialized_def_type: _,
type_arguments: pat_type_arguments,
lambda_set_variables: pat_lambda_set_variables,
} => match &annotation.value {
Type::Alias {
symbol,
kind: AliasKind::Opaque,
actual,
type_arguments,
lambda_set_variables,
infer_ext_in_output_types: _,
} if symbol == opaque
&& 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 typ = Loc::at(annotation.region, annotation_index);
headers.insert(*opaque, typ);
} => {
let typ = annotation.value;
let (_, argument_pat) = &**argument;
headers_from_annotation_help(
types,
constraints,
&argument_pat.value,
&Loc::at(annotation.region, actual),
headers,
)
match types[typ] {
TypeTag::OpaqueAlias { shared, actual } => {
let AliasShared {
symbol,
lambda_set_variables,
..
} = types[shared];
let type_arguments = types.get_type_arguments(typ);
if symbol == *opaque
&& type_arguments.len() == pat_type_arguments.len()
&& lambda_set_variables.len() == pat_lambda_set_variables.len()
{
let annotation_index = constraints.push_type(types, typ);
let typ = Loc::at(annotation.region, annotation_index);
headers.insert(*opaque, typ);
let (_, argument_pat) = &**argument;
headers_from_annotation_help(
types,
constraints,
&argument_pat.value,
&Loc::at(annotation.region, actual),
headers,
)
} else {
false
}
}
_ => false,
}
_ => false,
},
}
}
}