mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 08:11:12 +00:00
implement record update constraining
This commit is contained in:
parent
4147582738
commit
ccf66ab07c
4 changed files with 83 additions and 0 deletions
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue