mirror of
https://github.com/roc-lang/roc.git
synced 2024-11-11 05:34:11 +03:00
implement record update
This commit is contained in:
parent
16ec417324
commit
c4b4cb013e
@ -1035,8 +1035,6 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||
Reset(_) => todo!(),
|
||||
Reuse { .. } => todo!(),
|
||||
|
||||
Update { .. } => todo!(),
|
||||
|
||||
AccessAtIndex {
|
||||
index,
|
||||
structure,
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -337,7 +337,7 @@ impl<'a> BorrowInfState<'a> {
|
||||
self.own_var(*x);
|
||||
}
|
||||
}
|
||||
Update { .. } => todo!(),
|
||||
|
||||
FunctionCall {
|
||||
call_type,
|
||||
args,
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user