diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index f4fbe3314f..2d422b8f79 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -1035,8 +1035,6 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( Reset(_) => todo!(), Reuse { .. } => todo!(), - Update { .. } => todo!(), - AccessAtIndex { index, structure, diff --git a/compiler/gen/tests/gen_records.rs b/compiler/gen/tests/gen_records.rs index 0e4f5859fd..d9e5dcf9cd 100644 --- a/compiler/gen/tests/gen_records.rs +++ b/compiler/gen/tests/gen_records.rs @@ -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 + ); + } } diff --git a/compiler/mono/src/borrow.rs b/compiler/mono/src/borrow.rs index b5aec6c911..9d917abe89 100644 --- a/compiler/mono/src/borrow.rs +++ b/compiler/mono/src/borrow.rs @@ -337,7 +337,7 @@ impl<'a> BorrowInfState<'a> { self.own_var(*x); } } - Update { .. } => todo!(), + FunctionCall { call_type, args, diff --git a/compiler/mono/src/inc_dec.rs b/compiler/mono/src/inc_dec.rs index 43774b89f2..82e7f51b7f 100644 --- a/compiler/mono/src/inc_dec.rs +++ b/compiler/mono/src/inc_dec.rs @@ -100,13 +100,6 @@ pub fn occuring_variables_expr(expr: &Expr<'_>, result: &mut MutSet) { 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); diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index ee4a3d2a8e..570a9cac42 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -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, - } => 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 - } - } } }