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::boolean_algebra::Bool;
use roc_types::solved_types::{BuiltinAlias, SolvedBool, SolvedType}; use roc_types::solved_types::{BuiltinAlias, SolvedBool, SolvedType};
use roc_types::subs::{VarId, VarStore, Variable}; 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>; 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) Type::Variable(var)
} }
Record { fields, ext } => { Record { fields, ext } => {
use RecordField::*;
let mut new_fields = SendMap::default(); let mut new_fields = SendMap::default();
for (label, typ) in fields { for (label, field) in fields {
new_fields.insert(label.clone(), to_type(&typ, free_vars, var_store)); 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))) 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::constraint::Constraint;
use roc_can::expected::{Expected, PExpected}; use roc_can::expected::{Expected, PExpected};
use roc_can::pattern::Pattern::{self, *}; 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_collections::all::{Index, SendMap};
use roc_module::ident::Lowercase; use roc_module::ident::Lowercase;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_region::all::{Located, Region}; use roc_region::all::{Located, Region};
use roc_types::subs::Variable; 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 struct PatternState {
pub headers: SendMap<Symbol, Located<Type>>, pub headers: SendMap<Symbol, Located<Type>>,
@ -61,12 +61,20 @@ fn headers_from_annotation_help(
RecordDestructure { destructs, .. } => match annotation.value.shallow_dealias() { RecordDestructure { destructs, .. } => match annotation.value.shallow_dealias() {
Type::Record(fields, _) => { Type::Record(fields, _) => {
for destruct in destructs { for loc_destruct in destructs {
// NOTE ignores the .guard field. let destruct = &loc_destruct.value;
if let Some(field_type) = fields.get(&destruct.value.label) {
// 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( headers.insert(
destruct.value.symbol, destruct.symbol,
Located::at(annotation.region, field_type.clone()), Located::at(annotation.region, field_type.clone().into_inner()),
); );
} else { } else {
return false; return false;
@ -179,7 +187,7 @@ pub fn constrain_pattern(
state.vars.push(*ext_var); state.vars.push(*ext_var);
let ext_type = Type::Variable(*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 { for Located {
value: value:
@ -187,7 +195,7 @@ pub fn constrain_pattern(
var, var,
label, label,
symbol, symbol,
guard, typ,
}, },
.. ..
} in destructs } in destructs
@ -201,23 +209,36 @@ pub fn constrain_pattern(
.insert(*symbol, Located::at(region, pat_type.clone())); .insert(*symbol, Located::at(region, pat_type.clone()));
} }
field_types.insert(label.clone(), pat_type.clone()); let field_type = match typ {
DestructType::Guard(guard_var, loc_guard) => {
state.constraints.push(Constraint::Pattern(
region,
PatternCategory::PatternGuard,
Type::Variable(*guard_var),
PExpected::ForReason(
PReason::PatternGuard,
pat_type.clone(),
loc_guard.region,
),
));
state.vars.push(*guard_var);
if let Some((guard_var, loc_guard)) = guard { constrain_pattern(&loc_guard.value, loc_guard.region, expected, state);
state.constraints.push(Constraint::Pattern(
region,
PatternCategory::PatternGuard,
Type::Variable(*guard_var),
PExpected::ForReason(
PReason::PatternGuard,
pat_type.clone(),
loc_guard.region,
),
));
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); state.vars.push(*var);
} }