implement record update constraining

This commit is contained in:
Folkert 2020-01-05 01:04:32 +01:00
parent 4147582738
commit ccf66ab07c
4 changed files with 83 additions and 0 deletions

View file

@ -94,6 +94,15 @@ pub enum Expr {
field: Lowercase, field: Lowercase,
}, },
Update {
record_var: Variable,
ext_var: Variable,
// TODO allow qualified names here
symbol: Symbol,
name: Lowercase,
updates: SendMap<Lowercase, FieldUpdate>,
},
// Sum Types // Sum Types
Tag(Box<str>, Vec<Expr>), Tag(Box<str>, Vec<Expr>),
@ -101,6 +110,14 @@ pub enum Expr {
RuntimeError(RuntimeError), RuntimeError(RuntimeError),
} }
#[derive(Clone, Debug, PartialEq)]
pub struct FieldUpdate {
pub var: Variable,
// I assume this is the region of the full `foo = f bar`, rather than just the rhs
pub region: Region,
pub loc_expr: Box<Located<Expr>>,
}
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Recursive { pub enum Recursive {
Recursive, Recursive,

View file

@ -1,6 +1,7 @@
use crate::can::def::Declaration; use crate::can::def::Declaration;
use crate::can::def::Def; use crate::can::def::Def;
use crate::can::expr::Expr::{self, *}; use crate::can::expr::Expr::{self, *};
use crate::can::expr::FieldUpdate;
use crate::can::ident::Lowercase; use crate::can::ident::Lowercase;
use crate::can::pattern::Pattern; use crate::can::pattern::Pattern;
use crate::can::symbol::Symbol; use crate::can::symbol::Symbol;
@ -103,6 +104,51 @@ pub fn constrain_expr(
exists(field_vars, And(constraints)) exists(field_vars, And(constraints))
} }
} }
Update {
record_var,
ext_var,
name,
symbol,
updates,
} => {
let mut fields: SendMap<Lowercase, Type> = SendMap::default();
let mut vars = Vec::with_capacity(updates.len() + 2);
let mut cons = Vec::with_capacity(updates.len() + 1);
for (field_name, FieldUpdate { var, loc_expr, .. }) in updates.clone() {
let (var, tipe, con) =
constrain_field_update(rigids, var, region, field_name.clone(), &loc_expr);
fields.insert(field_name, tipe);
vars.push(var);
cons.push(con);
}
let fields_type = Type::Record(fields.clone(), Box::new(Type::Variable(*ext_var)));
let record_type = Type::Variable(*record_var);
// NOTE from elm compiler: fields_type is separate so that Error propagates better
let fields_con = Eq(record_type.clone(), NoExpectation(fields_type), region);
let record_con = Eq(record_type.clone(), expected, region);
vars.push(*record_var);
vars.push(*ext_var);
cons.push(record_con);
let con = Lookup(
symbol.clone(),
ForReason(
Reason::RecordUpdateKeys(name.clone(), fields),
record_type,
region,
),
region,
);
cons.push(con);
cons.push(fields_con);
exists(vars, And(cons))
}
Str(_) | BlockStr(_) => Eq(str_type(), expected, region), Str(_) | BlockStr(_) => Eq(str_type(), expected, region),
List(list_var, loc_elems) => { List(list_var, loc_elems) => {
if loc_elems.is_empty() { if loc_elems.is_empty() {
@ -768,3 +814,19 @@ pub fn create_letrec_constraint(
})), })),
})) }))
} }
#[inline(always)]
fn constrain_field_update(
rigids: &Rigids,
var: Variable,
region: Region,
field: Lowercase,
loc_expr: &Located<Expr>,
) -> (Variable, Type, Constraint) {
let field_type = Type::Variable(var);
let reason = Reason::RecordUpdateValue(field);
let expected = ForReason(reason, field_type.clone(), region);
let con = constrain_expr(rigids, loc_expr.region, &loc_expr.value, expected);
(var, field_type, con)
}

View file

@ -273,6 +273,8 @@ pub enum Reason {
InterpolatedStringVar, InterpolatedStringVar,
WhenBranch { index: usize }, WhenBranch { index: usize },
ElemInList, ElemInList,
RecordUpdateValue(Lowercase),
RecordUpdateKeys(Lowercase, SendMap<Lowercase, Type>),
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]

View file

@ -635,6 +635,8 @@ pub fn canonicalize_expr(
(output, And(constraints)) (output, And(constraints))
} }
Update { .. } => panic!("TODO implement record update for uniq"),
Access { Access {
ext_var, ext_var,
field_var, field_var,