diff --git a/compiler/gen/src/crane/build.rs b/compiler/gen/src/crane/build.rs index 7ca673800c..6f7dde44b4 100644 --- a/compiler/gen/src/crane/build.rs +++ b/compiler/gen/src/crane/build.rs @@ -285,40 +285,19 @@ pub fn build_expr<'a, B: Backend>( Access { label, field_layout, - struct_layout: Layout::Struct(fields), + struct_layout: Layout::Struct(sorted_fields), record, } => { let cfg = env.cfg; - // Reconstruct the struct to determine the combined layout - // TODO get rid of clones - let mut reconstructed_struct_layout = - Vec::with_capacity_in(fields.len() + 1, env.arena); - for field in fields.iter() { - reconstructed_struct_layout.push(field.clone()); - } - reconstructed_struct_layout.push((label.clone(), field_layout.clone())); - reconstructed_struct_layout.sort_by(|a, b| { - a.0.partial_cmp(&b.0) - .expect("TODO: failed to sort struct fields in crane access") - }); - // Find the offset we are trying to access let mut offset = 0; - for (local_label, layout) in reconstructed_struct_layout.iter() { + for (local_label, local_field_layout) in sorted_fields.iter() { if local_label == label { break; } - let field_size = match layout { - Layout::Builtin(Builtin::Int64) => std::mem::size_of::(), - _ => panic!( - "Missing struct field size in offset calculation for struct access for {:?}", - layout - ), - }; - - offset += field_size; + offset += local_field_layout.stack_size(ptr_bytes); } let offset = i32::try_from(offset) @@ -326,10 +305,11 @@ pub fn build_expr<'a, B: Backend>( let mem_flags = MemFlags::new(); let record = build_expr(env, scope, module, builder, record, procs); + let field_type = type_from_layout(cfg, field_layout); builder .ins() - .load(cfg.pointer_type(), mem_flags, record, Offset32::new(offset)) + .load(field_type, mem_flags, record, Offset32::new(offset)) } AccessAtIndex { index, diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index 35ea443b69..a9eb368e75 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -496,26 +496,14 @@ pub fn build_expr<'a, 'ctx, 'env>( } Access { label, - field_layout, - struct_layout: Layout::Struct(fields), + struct_layout: Layout::Struct(sorted_fields), record, + .. } => { let builder = env.builder; - // Reconstruct struct layout - let mut reconstructed_struct_layout = - Vec::with_capacity_in(fields.len() + 1, env.arena); - for field in fields.iter() { - reconstructed_struct_layout.push(field.clone()); - } - reconstructed_struct_layout.push((label.clone(), field_layout.clone())); - reconstructed_struct_layout.sort_by(|a, b| { - a.0.partial_cmp(&b.0) - .expect("TODO: failed to sort struct fields in crane access") - }); - // Get index - let index = reconstructed_struct_layout + let index = sorted_fields .iter() .position(|(local_label, _)| local_label == label) .unwrap() as u32; // TODO diff --git a/compiler/gen/tests/test_gen.rs b/compiler/gen/tests/test_gen.rs index 529607d7c9..6c45eee900 100644 --- a/compiler/gen/tests/test_gen.rs +++ b/compiler/gen/tests/test_gen.rs @@ -1618,4 +1618,43 @@ mod test_gen { i64 ); } + + #[test] + fn f64_record() { + assert_evals_to!( + indoc!( + r#" + rec = { y: 17.2, x: 15.1, z: 19.3 } + + rec.x + "# + ), + 15.1, + f64 + ); + + assert_evals_to!( + indoc!( + r#" + rec = { y: 17.2, x: 15.1, z: 19.3 } + + rec.y + "# + ), + 17.2, + f64 + ); + + assert_evals_to!( + indoc!( + r#" + rec = { y: 17.2, x: 15.1, z: 19.3 } + + rec.z + "# + ), + 19.3, + f64 + ); + } } diff --git a/compiler/reporting/src/report.rs b/compiler/reporting/src/report.rs index e264ed9aff..484a5a6b3e 100644 --- a/compiler/reporting/src/report.rs +++ b/compiler/reporting/src/report.rs @@ -11,6 +11,32 @@ pub struct Report { pub text: ReportText, } +pub struct Palette { + pub primary: Color, + pub error: Color, +} + +pub enum Color { + White, + Red, +} + +pub const DEFAULT_PALETTE: Palette = Palette { + primary: Color::White, + error: Color::Red, +}; + +impl Color { + pub fn render(self, str: &str) -> String { + use Color::*; + + match self { + Red => red(str), + White => white(str), + } + } +} + pub fn can_problem(filename: PathBuf, problem: Problem) -> Report { let mut texts = Vec::new(); @@ -77,6 +103,28 @@ fn newline() -> ReportText { plain_text("\n") } +fn red(str: &str) -> String { + let mut buf = String::new(); + + buf.push_str("\u{001b}[31m"); + buf.push_str(str); + buf.push_str("\u{001b}[0m"); + + buf +} + +fn white(str: &str) -> String { + let mut buf = String::new(); + + buf.push_str("\u{001b}[31m"); + buf.push_str(str); + buf.push_str(RESET); + + buf +} + +const RESET: &str = "\u{001b}[0m"; + impl ReportText { /// Render to CI console output, where no colors are available. pub fn render_ci( @@ -116,12 +164,12 @@ impl ReportText { Region(region) => { for i in region.start_line..=region.end_line { buf.push_str(i.to_string().as_str()); - buf.push_str(" |"); + buf.push_str(" ┆"); let line = src_lines[i as usize]; if !line.is_empty() { - buf.push(' '); + buf.push_str(" "); buf.push_str(src_lines[i as usize]); } @@ -144,13 +192,21 @@ impl ReportText { /// Render to a color terminal using ANSI escape sequences pub fn render_color_terminal( &self, - _buf: &mut String, + buf: &mut String, _subs: &mut Subs, _home: ModuleId, _src_lines: &[&str], _interns: &Interns, + palette: Palette, ) { - // TODO do the same stuff as render_ci, but with colors via ANSI terminal escape codes! - // Examples of how to do this are in the source code of https://github.com/rtfeldman/console-print + use ReportText::*; + + match self { + Plain(string) => { + buf.push_str(&palette.primary.render(string)); + } + + _ => panic!("TODO implement more ReportTexts in render color terminal"), + } } } diff --git a/compiler/reporting/tests/test_reporting.rs b/compiler/reporting/tests/test_reporting.rs index 12d5f8bbb5..87f07a800f 100644 --- a/compiler/reporting/tests/test_reporting.rs +++ b/compiler/reporting/tests/test_reporting.rs @@ -11,7 +11,7 @@ mod helpers; mod test_report { use crate::helpers::test_home; use roc_module::symbol::{Interns, ModuleId}; - use roc_reporting::report::{can_problem, plain_text, Report, ReportText}; + use roc_reporting::report::{can_problem, plain_text, Report, ReportText, DEFAULT_PALETTE}; use roc_types::pretty_print::name_all_type_vars; use roc_types::subs::Subs; use roc_types::types; @@ -82,6 +82,38 @@ mod test_report { assert_eq!(buf, expected_rendering); } + fn report_renders_in_color_from_src(src: &str, report: Report, expected_rendering: &str) { + let (_type_problems, _can_problems, mut subs, home, interns) = infer_expr_help(src); + let mut buf: String = String::new(); + let src_lines: Vec<&str> = src.split('\n').collect(); + + report.text.render_color_terminal( + &mut buf, + &mut subs, + home, + &src_lines, + &interns, + DEFAULT_PALETTE, + ); + + assert_eq!(buf, expected_rendering); + } + + fn report_renders_in_color(report: Report, expected_rendering: &str) { + report_renders_in_color_from_src( + indoc!( + r#" + x = 1 + y = 2 + + x + "# + ), + report, + expected_rendering, + ) + } + fn report_renders_as(report: Report, expected_rendering: &str) { report_renders_as_from_src( indoc!( @@ -202,15 +234,23 @@ mod test_report { buf, indoc!( r#" - y is not used anywhere in your code. + y is not used anywhere in your code. - 1 | y = 2 + 1 ┆ y = 2 - If you didn't intend on using y then remove it so future readers of your code don't wonder why it is there."# + If you didn't intend on using y then remove it so future readers of your code don't wonder why it is there."# ) ); } + #[test] + fn report_in_color() { + report_renders_in_color( + to_simple_report(plain_text("y")), + "\u{001b}[31my\u{001b}[0m", + ); + } + #[test] fn report_region() { report_renders_as_from_src( @@ -218,7 +258,7 @@ mod test_report { r#" x = 1 y = 2 - f = \a -> a + 4 + f = \a -> a + 4 f x "# @@ -231,9 +271,9 @@ mod test_report { })), indoc!( r#" - 1 | y = 2 - 2 | f = \a -> a + 4 - 3 |"# + 1 ┆ y = 2 + 2 ┆ f = \a -> a + 4 + 3 ┆"# ), ); }