implement record update

This commit is contained in:
Folkert 2020-10-04 00:06:14 +02:00
parent 16ec417324
commit c4b4cb013e
5 changed files with 150 additions and 72 deletions

View File

@ -1035,8 +1035,6 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
Reset(_) => todo!(),
Reuse { .. } => todo!(),
Update { .. } => todo!(),
AccessAtIndex {
index,
structure,

View File

@ -410,9 +410,9 @@ mod gen_records {
{ x: Blue, y ? 3 } -> y
{ x: Red, y ? 5 } -> y
a = f { x: Blue, y: 7 }
a = f { x: Blue, y: 7 }
b = f { x: Blue }
c = f { x: Red, y: 11 }
c = f { x: Red, y: 11 }
d = f { x: Red }
a * b * c * d
@ -617,7 +617,7 @@ mod gen_records {
assert_evals_to!(
indoc!(
r#"
{ a: 3.14, b: 0x1 }
{ a: 3.14, b: 0x1 }
"#
),
(3.14, 0x1),
@ -689,4 +689,47 @@ mod gen_records {
i64
);
}
#[test]
fn accessor_single_element_record() {
assert_evals_to!(
indoc!(
r#"
.foo { foo: 4 }
"#
),
4,
i64
);
}
#[test]
fn update_record() {
assert_evals_to!(
indoc!(
r#"
rec = { foo: 42, bar: 6.28 }
{ rec & foo: rec.foo + 1 }
"#
),
(6.28, 43),
(f64, i64)
);
}
#[test]
fn update_single_element_record() {
assert_evals_to!(
indoc!(
r#"
rec = { foo: 42}
{ rec & foo: rec.foo + 1 }
"#
),
43,
i64
);
}
}

View File

@ -337,7 +337,7 @@ impl<'a> BorrowInfState<'a> {
self.own_var(*x);
}
}
Update { .. } => todo!(),
FunctionCall {
call_type,
args,

View File

@ -100,13 +100,6 @@ pub fn occuring_variables_expr(expr: &Expr<'_>, result: &mut MutSet<Symbol>) {
result.insert(*symbol);
}
Update {
structure, updates, ..
} => {
result.insert(*structure);
result.extend(updates.iter().map(|r| r.1));
}
FunctionCall { args, .. } => {
// NOTE thouth the function name does occur, it is a static constant in the program
// for liveness, it should not be included here.
@ -463,8 +456,6 @@ impl<'a> Context<'a> {
self.arena.alloc(Stmt::Let(z, v, l, b))
}
Update { .. } => todo!(),
RunLowLevel(op, args) => {
let ps = crate::borrow::lowlevel_borrow_signature(self.arena, op);
let b = self.add_dec_after_lowlevel(args, ps, b, b_live_vars);

View File

@ -687,11 +687,6 @@ pub enum Expr<'a> {
structure: Symbol,
wrapped: Wrapped,
},
Update {
structure: Symbol,
field_layouts: &'a [Layout<'a>],
updates: &'a [(u64, Symbol)],
},
Array {
elem_layout: Layout<'a>,
@ -852,23 +847,6 @@ impl<'a> Expr<'a> {
.text(format!("Index {} ", index))
.append(symbol_to_doc(alloc, *structure)),
Update {
structure, updates, ..
} => {
let it = updates.iter().map(|(index, symbol)| {
alloc
.text(format!(".{} => ", index))
.append(symbol_to_doc(alloc, *symbol))
});
alloc
.text("Update ")
.append(symbol_to_doc(alloc, *structure))
.append(alloc.text("{ "))
.append(alloc.intersperse(it, ", "))
.append(alloc.text(" }"))
}
RuntimeErrorFunction(s) => alloc.text(format!("ErrorFunction {}", s)),
}
}
@ -2041,11 +2019,109 @@ pub fn with_hole<'a>(
}
Update {
record_var, // Variable,
ext_var, // Variable,
symbol, // Symbol,
updates, // SendMap<Lowercase, Field>,
} => todo!("record access/accessor/update"),
record_var,
symbol: structure,
updates,
..
} => {
use FieldType::*;
enum FieldType<'a> {
CopyExisting(u64),
UpdateExisting(&'a roc_can::expr::Field),
};
// Strategy: turn a record update into the creation of a new record.
// This has the benefit that we don't need to do anything special for reference
// counting
let sorted_fields = crate::layout::sort_record_fields(env.arena, record_var, env.subs);
let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena);
let mut symbols = Vec::with_capacity_in(sorted_fields.len(), env.arena);
let mut fields = Vec::with_capacity_in(sorted_fields.len(), env.arena);
let mut current = 0;
for (label, opt_field_layout) in sorted_fields.into_iter() {
match opt_field_layout {
Err(_) => {
debug_assert!(!updates.contains_key(&label));
// this was an optional field, and now does not exist!
// do not increment `current`!
}
Ok(field_layout) => {
field_layouts.push(field_layout);
if let Some(field) = updates.get(&label) {
// TODO
let field_symbol =
possible_reuse_symbol(env, procs, &field.loc_expr.value);
fields.push(UpdateExisting(field));
symbols.push(field_symbol);
} else {
fields.push(CopyExisting(current));
symbols.push(env.unique_symbol());
}
current += 1;
}
}
}
let symbols = symbols.into_bump_slice();
let record_layout = layout_cache
.from_var(env.arena, record_var, env.subs)
.unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err));
let field_layouts = match &record_layout {
Layout::Struct(layouts) => *layouts,
other => arena.alloc([other.clone()]),
};
let wrapped = if field_layouts.len() == 1 {
Wrapped::SingleElementRecord
} else {
Wrapped::RecordOrSingleTagUnion
};
let expr = Expr::Struct(symbols);
let mut stmt = Stmt::Let(assigned, expr, record_layout, hole);
let it = field_layouts.iter().zip(symbols.iter()).zip(fields);
for ((field_layout, symbol), what_to_do) in it {
match what_to_do {
UpdateExisting(field) => {
stmt = assign_to_symbol(
env,
procs,
layout_cache,
field.var,
*field.loc_expr.clone(),
*symbol,
stmt,
);
}
CopyExisting(index) => {
let access_expr = Expr::AccessAtIndex {
structure,
index,
field_layouts,
wrapped,
};
stmt = Stmt::Let(
*symbol,
access_expr,
field_layout.clone(),
arena.alloc(stmt),
);
}
}
}
stmt
}
Closure {
function_type,
@ -2993,36 +3069,6 @@ fn substitute_in_expr<'a>(
}),
None => None,
},
Update {
structure,
field_layouts,
updates,
} => {
let mut did_change = false;
let new_updates = Vec::from_iter_in(
updates.iter().map(|(index, s)| match substitute(subs, *s) {
None => (*index, *s),
Some(s) => {
did_change = true;
(*index, s)
}
}),
arena,
);
if did_change {
let updates = new_updates.into_bump_slice();
Some(Update {
structure: *structure,
field_layouts,
updates,
})
} else {
None
}
}
}
}