Constrain optional record fields

This commit is contained in:
Richard Feldman 2020-07-18 13:12:04 -04:00
parent 8fc9a450b3
commit 1409421de2
2 changed files with 55 additions and 27 deletions

View File

@ -9,7 +9,7 @@ use roc_region::all::{Located, Region};
use roc_types::boolean_algebra::Bool;
use roc_types::solved_types::{BuiltinAlias, SolvedBool, SolvedType};
use roc_types::subs::{VarId, VarStore, Variable};
use roc_types::types::{Alias, Problem, Type};
use roc_types::types::{Alias, Problem, RecordField, Type};
pub type SubsByModule = MutMap<ModuleId, ExposedModuleTypes>;
@ -214,10 +214,17 @@ fn to_type(solved_type: &SolvedType, free_vars: &mut FreeVars, var_store: &mut V
Type::Variable(var)
}
Record { fields, ext } => {
use RecordField::*;
let mut new_fields = SendMap::default();
for (label, typ) in fields {
new_fields.insert(label.clone(), to_type(&typ, free_vars, var_store));
for (label, field) in fields {
let field_val = match field {
Required(typ) => Required(to_type(&typ, free_vars, var_store)),
Optional(typ) => Optional(to_type(&typ, free_vars, var_store)),
};
new_fields.insert(label.clone(), field_val);
}
Type::Record(new_fields, Box::new(to_type(ext, free_vars, var_store)))

View File

@ -2,13 +2,13 @@ use crate::builtins;
use roc_can::constraint::Constraint;
use roc_can::expected::{Expected, PExpected};
use roc_can::pattern::Pattern::{self, *};
use roc_can::pattern::RecordDestruct;
use roc_can::pattern::{DestructType, RecordDestruct};
use roc_collections::all::{Index, SendMap};
use roc_module::ident::Lowercase;
use roc_module::symbol::Symbol;
use roc_region::all::{Located, Region};
use roc_types::subs::Variable;
use roc_types::types::{Category, PReason, PatternCategory, Type};
use roc_types::types::{Category, PReason, PatternCategory, RecordField, Type};
pub struct PatternState {
pub headers: SendMap<Symbol, Located<Type>>,
@ -61,12 +61,20 @@ fn headers_from_annotation_help(
RecordDestructure { destructs, .. } => match annotation.value.shallow_dealias() {
Type::Record(fields, _) => {
for destruct in destructs {
// NOTE ignores the .guard field.
if let Some(field_type) = fields.get(&destruct.value.label) {
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(field_type) = fields.get(&destruct.label) {
headers.insert(
destruct.value.symbol,
Located::at(annotation.region, field_type.clone()),
destruct.symbol,
Located::at(annotation.region, field_type.clone().into_inner()),
);
} else {
return false;
@ -179,7 +187,7 @@ pub fn constrain_pattern(
state.vars.push(*ext_var);
let ext_type = Type::Variable(*ext_var);
let mut field_types: SendMap<Lowercase, Type> = SendMap::default();
let mut field_types: SendMap<Lowercase, RecordField<Type>> = SendMap::default();
for Located {
value:
@ -187,7 +195,7 @@ pub fn constrain_pattern(
var,
label,
symbol,
guard,
typ,
},
..
} in destructs
@ -201,9 +209,8 @@ pub fn constrain_pattern(
.insert(*symbol, Located::at(region, pat_type.clone()));
}
field_types.insert(label.clone(), pat_type.clone());
if let Some((guard_var, loc_guard)) = guard {
let field_type = match typ {
DestructType::Guard(guard_var, loc_guard) => {
state.constraints.push(Constraint::Pattern(
region,
PatternCategory::PatternGuard,
@ -217,7 +224,21 @@ pub fn constrain_pattern(
state.vars.push(*guard_var);
constrain_pattern(&loc_guard.value, loc_guard.region, expected, state);
RecordField::Required(pat_type)
}
DestructType::Optional(_var) => {
todo!("Add a constraint for the default value.");
// RecordField::Optional(pat_type)
}
DestructType::Required => {
// No extra constraints necessary.
RecordField::Required(pat_type)
}
};
field_types.insert(label.clone(), field_type);
state.vars.push(*var);
}