mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 22:34:45 +00:00
implement record update
This commit is contained in:
parent
16ec417324
commit
c4b4cb013e
5 changed files with 150 additions and 72 deletions
|
@ -1035,8 +1035,6 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
Reset(_) => todo!(),
|
Reset(_) => todo!(),
|
||||||
Reuse { .. } => todo!(),
|
Reuse { .. } => todo!(),
|
||||||
|
|
||||||
Update { .. } => todo!(),
|
|
||||||
|
|
||||||
AccessAtIndex {
|
AccessAtIndex {
|
||||||
index,
|
index,
|
||||||
structure,
|
structure,
|
||||||
|
|
|
@ -410,9 +410,9 @@ mod gen_records {
|
||||||
{ x: Blue, y ? 3 } -> y
|
{ x: Blue, y ? 3 } -> y
|
||||||
{ x: Red, y ? 5 } -> y
|
{ x: Red, y ? 5 } -> y
|
||||||
|
|
||||||
a = f { x: Blue, y: 7 }
|
a = f { x: Blue, y: 7 }
|
||||||
b = f { x: Blue }
|
b = f { x: Blue }
|
||||||
c = f { x: Red, y: 11 }
|
c = f { x: Red, y: 11 }
|
||||||
d = f { x: Red }
|
d = f { x: Red }
|
||||||
|
|
||||||
a * b * c * d
|
a * b * c * d
|
||||||
|
@ -617,7 +617,7 @@ mod gen_records {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
{ a: 3.14, b: 0x1 }
|
{ a: 3.14, b: 0x1 }
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
(3.14, 0x1),
|
(3.14, 0x1),
|
||||||
|
@ -689,4 +689,47 @@ mod gen_records {
|
||||||
i64
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -337,7 +337,7 @@ impl<'a> BorrowInfState<'a> {
|
||||||
self.own_var(*x);
|
self.own_var(*x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Update { .. } => todo!(),
|
|
||||||
FunctionCall {
|
FunctionCall {
|
||||||
call_type,
|
call_type,
|
||||||
args,
|
args,
|
||||||
|
|
|
@ -100,13 +100,6 @@ pub fn occuring_variables_expr(expr: &Expr<'_>, result: &mut MutSet<Symbol>) {
|
||||||
result.insert(*symbol);
|
result.insert(*symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
Update {
|
|
||||||
structure, updates, ..
|
|
||||||
} => {
|
|
||||||
result.insert(*structure);
|
|
||||||
result.extend(updates.iter().map(|r| r.1));
|
|
||||||
}
|
|
||||||
|
|
||||||
FunctionCall { args, .. } => {
|
FunctionCall { args, .. } => {
|
||||||
// NOTE thouth the function name does occur, it is a static constant in the program
|
// NOTE thouth the function name does occur, it is a static constant in the program
|
||||||
// for liveness, it should not be included here.
|
// 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))
|
self.arena.alloc(Stmt::Let(z, v, l, b))
|
||||||
}
|
}
|
||||||
|
|
||||||
Update { .. } => todo!(),
|
|
||||||
|
|
||||||
RunLowLevel(op, args) => {
|
RunLowLevel(op, args) => {
|
||||||
let ps = crate::borrow::lowlevel_borrow_signature(self.arena, op);
|
let ps = crate::borrow::lowlevel_borrow_signature(self.arena, op);
|
||||||
let b = self.add_dec_after_lowlevel(args, ps, b, b_live_vars);
|
let b = self.add_dec_after_lowlevel(args, ps, b, b_live_vars);
|
||||||
|
|
|
@ -687,11 +687,6 @@ pub enum Expr<'a> {
|
||||||
structure: Symbol,
|
structure: Symbol,
|
||||||
wrapped: Wrapped,
|
wrapped: Wrapped,
|
||||||
},
|
},
|
||||||
Update {
|
|
||||||
structure: Symbol,
|
|
||||||
field_layouts: &'a [Layout<'a>],
|
|
||||||
updates: &'a [(u64, Symbol)],
|
|
||||||
},
|
|
||||||
|
|
||||||
Array {
|
Array {
|
||||||
elem_layout: Layout<'a>,
|
elem_layout: Layout<'a>,
|
||||||
|
@ -852,23 +847,6 @@ impl<'a> Expr<'a> {
|
||||||
.text(format!("Index {} ", index))
|
.text(format!("Index {} ", index))
|
||||||
.append(symbol_to_doc(alloc, *structure)),
|
.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)),
|
RuntimeErrorFunction(s) => alloc.text(format!("ErrorFunction {}", s)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2041,11 +2019,109 @@ pub fn with_hole<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
Update {
|
Update {
|
||||||
record_var, // Variable,
|
record_var,
|
||||||
ext_var, // Variable,
|
symbol: structure,
|
||||||
symbol, // Symbol,
|
updates,
|
||||||
updates, // SendMap<Lowercase, Field>,
|
..
|
||||||
} => todo!("record access/accessor/update"),
|
} => {
|
||||||
|
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 {
|
Closure {
|
||||||
function_type,
|
function_type,
|
||||||
|
@ -2993,36 +3069,6 @@ fn substitute_in_expr<'a>(
|
||||||
}),
|
}),
|
||||||
None => None,
|
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue