diff --git a/crates/compiler/builtins/bitcode/src/utils.zig b/crates/compiler/builtins/bitcode/src/utils.zig index 6a77a13d4d..61a2a3ee85 100644 --- a/crates/compiler/builtins/bitcode/src/utils.zig +++ b/crates/compiler/builtins/bitcode/src/utils.zig @@ -20,11 +20,11 @@ extern fn roc_realloc(c_ptr: *anyopaque, new_size: usize, old_size: usize, align // This should never be passed a null pointer. extern fn roc_dealloc(c_ptr: *anyopaque, alignment: u32) callconv(.C) void; -extern fn roc_dbg(file_path: *anyopaque, message: *anyopaque) callconv(.C) void; +extern fn roc_dbg(loc: *anyopaque, src: *anyopaque, message: *anyopaque) callconv(.C) void; // Since roc_dbg is never used by the builtins, we need at export a function that uses it to stop DCE. -pub fn test_dbg(file_path: *anyopaque, message: *anyopaque) callconv(.C) void { - roc_dbg(file_path, message); +pub fn test_dbg(loc: *anyopaque, src: *anyopaque, message: *anyopaque) callconv(.C) void { + roc_dbg(loc, src, message); } extern fn kill(pid: c_int, sig: c_int) c_int; @@ -47,9 +47,10 @@ fn testing_roc_mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int return mmap(addr, length, prot, flags, fd, offset); } -fn testing_roc_dbg(file_path: *anyopaque, message: *anyopaque) callconv(.C) void { +fn testing_roc_dbg(loc: *anyopaque, src: *anyopaque, message: *anyopaque) callconv(.C) void { _ = message; - _ = file_path; + _ = src; + _ = loc; } comptime { diff --git a/crates/compiler/can/src/copy.rs b/crates/compiler/can/src/copy.rs index 73f535f710..fb806d456c 100644 --- a/crates/compiler/can/src/copy.rs +++ b/crates/compiler/can/src/copy.rs @@ -670,11 +670,15 @@ fn deep_copy_expr_help(env: &mut C, copied: &mut Vec, expr }, Dbg { + source_location, + source, loc_message, loc_continuation, variable, symbol, } => Dbg { + source_location: source_location.clone(), + source: source.clone(), loc_message: Box::new(loc_message.map(|e| go_help!(e))), loc_continuation: Box::new(loc_continuation.map(|e| go_help!(e))), variable: sub!(*variable), diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index 474a721bdb..1cacdb4e9a 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -269,6 +269,8 @@ pub enum Expr { }, Dbg { + source_location: Box, + source: Box, loc_message: Box>, loc_continuation: Box>, variable: Variable, @@ -1249,7 +1251,7 @@ pub fn canonicalize_expr<'a>( ast::Expr::Dbg(_, _) => { internal_error!("Dbg should have been desugared by now") } - ast::Expr::LowLevelDbg(message, continuation) => { + ast::Expr::LowLevelDbg(source_location, source, message, continuation) => { let mut output = Output::default(); let (loc_message, output1) = @@ -1276,6 +1278,8 @@ pub fn canonicalize_expr<'a>( ( Dbg { + source_location: (*source_location).into(), + source: (*source).into(), loc_message: Box::new(loc_message), loc_continuation: Box::new(loc_continuation), variable: var_store.fresh(), @@ -2097,6 +2101,8 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr { } Dbg { + source_location, + source, loc_message, loc_continuation, variable, @@ -2113,6 +2119,8 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr { }; Dbg { + source_location, + source, loc_message: Box::new(loc_message), loc_continuation: Box::new(loc_continuation), variable, @@ -2398,7 +2406,7 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool { | ast::Expr::MalformedClosure => true, // Newlines are disallowed inside interpolation, and these all require newlines ast::Expr::Dbg(_, _) - | ast::Expr::LowLevelDbg(_, _) + | ast::Expr::LowLevelDbg(_, _, _, _) | ast::Expr::Defs(_, _) | ast::Expr::Expect(_, _) | ast::Expr::When(_, _) diff --git a/crates/compiler/can/src/module.rs b/crates/compiler/can/src/module.rs index 729d53c4aa..1bb5cc4bec 100644 --- a/crates/compiler/can/src/module.rs +++ b/crates/compiler/can/src/module.rs @@ -275,6 +275,8 @@ pub fn canonicalize_module_defs<'a>( loc_defs: &'a mut Defs<'a>, header_type: &roc_parse::header::HeaderType, home: ModuleId, + module_path: &str, + src: &'a str, module_ids: &'a ModuleIds, exposed_ident_ids: IdentIds, dep_idents: &'a IdentIdsByModule, @@ -310,7 +312,7 @@ pub fn canonicalize_module_defs<'a>( // visited a BinOp node we'd recursively try to apply this to each of its nested // operators, and then again on *their* nested operators, ultimately applying the // rules multiple times unnecessarily. - crate::operator::desugar_defs(arena, loc_defs); + crate::operator::desugar_defs(arena, loc_defs, src, &mut None, module_path); let mut rigid_variables = RigidVariables::default(); diff --git a/crates/compiler/can/src/operator.rs b/crates/compiler/can/src/operator.rs index ba1cc6614c..d80760d5c9 100644 --- a/crates/compiler/can/src/operator.rs +++ b/crates/compiler/can/src/operator.rs @@ -11,7 +11,7 @@ use roc_parse::ast::{ AssignedField, Collection, Pattern, RecordBuilderField, StrLiteral, StrSegment, ValueDef, WhenBranch, }; -use roc_region::all::{Loc, Region}; +use roc_region::all::{LineInfo, Loc, Region}; // BinOp precedence logic adapted from Gluon by Markus Westerlind // https://github.com/gluon-lang/gluon - license information can be found in @@ -67,13 +67,19 @@ fn new_op_call_expr<'a>( Loc { region, value } } -fn desugar_value_def<'a>(arena: &'a Bump, def: &'a ValueDef<'a>) -> ValueDef<'a> { +fn desugar_value_def<'a>( + arena: &'a Bump, + def: &'a ValueDef<'a>, + src: &'a str, + line_info: &mut Option, + module_path: &str, +) -> ValueDef<'a> { use ValueDef::*; match def { Body(loc_pattern, loc_expr) => Body( - desugar_loc_pattern(arena, loc_pattern), - desugar_expr(arena, loc_expr), + desugar_loc_pattern(arena, loc_pattern, src, line_info, module_path), + desugar_expr(arena, loc_expr, src, line_info, module_path), ), ann @ Annotation(_, _) => *ann, AnnotatedBody { @@ -87,13 +93,14 @@ fn desugar_value_def<'a>(arena: &'a Bump, def: &'a ValueDef<'a>) -> ValueDef<'a> ann_type, comment: *comment, body_pattern, - body_expr: desugar_expr(arena, body_expr), + body_expr: desugar_expr(arena, body_expr, src, line_info, module_path), }, Dbg { condition, preceding_comment, } => { - let desugared_condition = &*arena.alloc(desugar_expr(arena, condition)); + let desugared_condition = + &*arena.alloc(desugar_expr(arena, condition, src, line_info, module_path)); Dbg { condition: desugared_condition, preceding_comment: *preceding_comment, @@ -103,7 +110,8 @@ fn desugar_value_def<'a>(arena: &'a Bump, def: &'a ValueDef<'a>) -> ValueDef<'a> condition, preceding_comment, } => { - let desugared_condition = &*arena.alloc(desugar_expr(arena, condition)); + let desugared_condition = + &*arena.alloc(desugar_expr(arena, condition, src, line_info, module_path)); Expect { condition: desugared_condition, preceding_comment: *preceding_comment, @@ -113,7 +121,8 @@ fn desugar_value_def<'a>(arena: &'a Bump, def: &'a ValueDef<'a>) -> ValueDef<'a> condition, preceding_comment, } => { - let desugared_condition = &*arena.alloc(desugar_expr(arena, condition)); + let desugared_condition = + &*arena.alloc(desugar_expr(arena, condition, src, line_info, module_path)); ExpectFx { condition: desugared_condition, preceding_comment: *preceding_comment, @@ -122,15 +131,27 @@ fn desugar_value_def<'a>(arena: &'a Bump, def: &'a ValueDef<'a>) -> ValueDef<'a> } } -pub fn desugar_defs<'a>(arena: &'a Bump, defs: &mut roc_parse::ast::Defs<'a>) { +pub fn desugar_defs<'a>( + arena: &'a Bump, + defs: &mut roc_parse::ast::Defs<'a>, + src: &'a str, + line_info: &mut Option, + module_path: &str, +) { for value_def in defs.value_defs.iter_mut() { - *value_def = desugar_value_def(arena, arena.alloc(*value_def)); + *value_def = desugar_value_def(arena, arena.alloc(*value_def), src, line_info, module_path); } } /// Reorder the expression tree based on operator precedence and associativity rules, /// then replace the BinOp nodes with Apply nodes. Also drop SpaceBefore and SpaceAfter nodes. -pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc>) -> &'a Loc> { +pub fn desugar_expr<'a>( + arena: &'a Bump, + loc_expr: &'a Loc>, + src: &'a str, + line_info: &mut Option, + module_path: &str, +) -> &'a Loc> { match &loc_expr.value { Float(..) | Num(..) @@ -153,16 +174,22 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc>) -> &'a Loc StrLiteral::PlainLine(_) => loc_expr, StrLiteral::Line(segments) => { let region = loc_expr.region; - let value = Str(StrLiteral::Line(desugar_str_segments(arena, segments))); + let value = Str(StrLiteral::Line(desugar_str_segments( + arena, + segments, + src, + line_info, + module_path, + ))); arena.alloc(Loc { region, value }) } StrLiteral::Block(lines) => { let region = loc_expr.region; let new_lines = Vec::from_iter_in( - lines - .iter() - .map(|segments| desugar_str_segments(arena, segments)), + lines.iter().map(|segments| { + desugar_str_segments(arena, segments, src, line_info, module_path) + }), arena, ); let value = Str(StrLiteral::Block(new_lines.into_bump_slice())); @@ -177,7 +204,17 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc>) -> &'a Loc region, value: **sub_expr, }; - let value = TupleAccess(&desugar_expr(arena, arena.alloc(loc_sub_expr)).value, paths); + let value = TupleAccess( + &desugar_expr( + arena, + arena.alloc(loc_sub_expr), + src, + line_info, + module_path, + ) + .value, + paths, + ); arena.alloc(Loc { region, value }) } @@ -187,7 +224,17 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc>) -> &'a Loc region, value: **sub_expr, }; - let value = RecordAccess(&desugar_expr(arena, arena.alloc(loc_sub_expr)).value, paths); + let value = RecordAccess( + &desugar_expr( + arena, + arena.alloc(loc_sub_expr), + src, + line_info, + module_path, + ) + .value, + paths, + ); arena.alloc(Loc { region, value }) } @@ -195,7 +242,7 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc>) -> &'a Loc let mut new_items = Vec::with_capacity_in(items.len(), arena); for item in items.iter() { - new_items.push(desugar_expr(arena, item)); + new_items.push(desugar_expr(arena, item, src, line_info, module_path)); } let new_items = new_items.into_bump_slice(); let value: Expr<'a> = List(items.replace_items(new_items)); @@ -205,32 +252,47 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc>) -> &'a Loc value, }) } - Record(fields) => arena.alloc(Loc { - region: loc_expr.region, - value: Record(fields.map_items(arena, |field| { - let value = desugar_field(arena, &field.value); - Loc { + Record(fields) => { + let mut allocated = Vec::with_capacity_in(fields.len(), arena); + for field in fields.iter() { + let value = desugar_field(arena, &field.value, src, line_info, module_path); + allocated.push(Loc { value, region: field.region, - } - })), - }), - Tuple(fields) => arena.alloc(Loc { - region: loc_expr.region, - value: Tuple(fields.map_items(arena, |field| desugar_expr(arena, field))), - }), + }); + } + let fields = fields.replace_items(allocated.into_bump_slice()); + arena.alloc(Loc { + region: loc_expr.region, + value: Record(fields), + }) + } + Tuple(fields) => { + let mut allocated = Vec::with_capacity_in(fields.len(), arena); + for field in fields.iter() { + let expr = desugar_expr(arena, field, src, line_info, module_path); + allocated.push(expr); + } + let fields = fields.replace_items(allocated.into_bump_slice()); + arena.alloc(Loc { + region: loc_expr.region, + value: Tuple(fields), + }) + } RecordUpdate { fields, update } => { // NOTE the `update` field is always a `Var { .. }`, we only desugar it to get rid of // any spaces before/after - let new_update = desugar_expr(arena, update); + let new_update = desugar_expr(arena, update, src, line_info, module_path); - let new_fields = fields.map_items(arena, |field| { - let value = desugar_field(arena, &field.value); - Loc { + let mut allocated = Vec::with_capacity_in(fields.len(), arena); + for field in fields.iter() { + let value = desugar_field(arena, &field.value, src, line_info, module_path); + allocated.push(Loc { value, region: field.region, - } - }); + }); + } + let new_fields = fields.replace_items(allocated.into_bump_slice()); arena.alloc(Loc { region: loc_expr.region, @@ -243,8 +305,8 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc>) -> &'a Loc Closure(loc_patterns, loc_ret) => arena.alloc(Loc { region: loc_expr.region, value: Closure( - desugar_loc_patterns(arena, loc_patterns), - desugar_expr(arena, loc_ret), + desugar_loc_patterns(arena, loc_patterns, src, line_info, module_path), + desugar_expr(arena, loc_ret, src, line_info, module_path), ), }), Backpassing(loc_patterns, loc_body, loc_ret) => { @@ -253,10 +315,11 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc>) -> &'a Loc // loc_ret // first desugar the body, because it may contain |> - let desugared_body = desugar_expr(arena, loc_body); + let desugared_body = desugar_expr(arena, loc_body, src, line_info, module_path); - let desugared_ret = desugar_expr(arena, loc_ret); - let desugared_loc_patterns = desugar_loc_patterns(arena, loc_patterns); + let desugared_ret = desugar_expr(arena, loc_ret, src, line_info, module_path); + let desugared_loc_patterns = + desugar_loc_patterns(arena, loc_patterns, src, line_info, module_path); let closure = Expr::Closure(desugared_loc_patterns, desugared_ret); let loc_closure = Loc::at(loc_expr.region, closure); @@ -289,12 +352,20 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc>) -> &'a Loc value: UnappliedRecordBuilder(loc_expr), region: loc_expr.region, }), - BinOps(lefts, right) => desugar_bin_ops(arena, loc_expr.region, lefts, right), + BinOps(lefts, right) => desugar_bin_ops( + arena, + loc_expr.region, + lefts, + right, + src, + line_info, + module_path, + ), Defs(defs, loc_ret) => { let mut defs = (*defs).clone(); - desugar_defs(arena, &mut defs); + desugar_defs(arena, &mut defs, src, line_info, module_path); - let loc_ret = desugar_expr(arena, loc_ret); + let loc_ret = desugar_expr(arena, loc_ret, src, line_info, module_path); arena.alloc(Loc::at(loc_expr.region, Defs(arena.alloc(defs), loc_ret))) } @@ -326,13 +397,17 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc>) -> &'a Loc } }; - desugared_args.push(desugar_expr(arena, arg)); + desugared_args.push(desugar_expr(arena, arg, src, line_info, module_path)); } let desugared_args = desugared_args.into_bump_slice(); let mut apply: &Loc = arena.alloc(Loc { - value: Apply(desugar_expr(arena, loc_fn), desugared_args, *called_via), + value: Apply( + desugar_expr(arena, loc_fn, src, line_info, module_path), + desugared_args, + *called_via, + ), region: loc_expr.region, }); @@ -341,7 +416,7 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc>) -> &'a Loc Some(apply_exprs) => { for expr in apply_exprs { - let desugared_expr = desugar_expr(arena, expr); + let desugared_expr = desugar_expr(arena, expr, src, line_info, module_path); let args = std::slice::from_ref(arena.alloc(apply)); @@ -356,15 +431,23 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc>) -> &'a Loc apply } When(loc_cond_expr, branches) => { - let loc_desugared_cond = &*arena.alloc(desugar_expr(arena, loc_cond_expr)); + let loc_desugared_cond = &*arena.alloc(desugar_expr( + arena, + loc_cond_expr, + src, + line_info, + module_path, + )); let mut desugared_branches = Vec::with_capacity_in(branches.len(), arena); for branch in branches.iter() { - let desugared_expr = desugar_expr(arena, &branch.value); - let desugared_patterns = desugar_loc_patterns(arena, branch.patterns); + let desugared_expr = + desugar_expr(arena, &branch.value, src, line_info, module_path); + let desugared_patterns = + desugar_loc_patterns(arena, branch.patterns, src, line_info, module_path); let desugared_guard = if let Some(guard) = &branch.guard { - Some(*desugar_expr(arena, guard)) + Some(*desugar_expr(arena, guard, src, line_info, module_path)) } else { None }; @@ -402,7 +485,8 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc>) -> &'a Loc }, }; let loc_fn_var = arena.alloc(Loc { region, value }); - let desugared_args = arena.alloc([desugar_expr(arena, loc_arg)]); + let desugared_args = + arena.alloc([desugar_expr(arena, loc_arg, src, line_info, module_path)]); arena.alloc(Loc { value: Apply(loc_fn_var, desugared_args, CalledVia::UnaryOp(op)), @@ -418,6 +502,9 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc>) -> &'a Loc value: **expr, region: loc_expr.region, }), + src, + line_info, + module_path, ) } ParensAround(expr) => { @@ -427,6 +514,9 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc>) -> &'a Loc value: **expr, region: loc_expr.region, }), + src, + line_info, + module_path, ); arena.alloc(Loc { @@ -436,14 +526,20 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc>) -> &'a Loc } If(if_thens, final_else_branch) => { // If does not get desugared into `when` so we can give more targeted error messages during type checking. - let desugared_final_else = &*arena.alloc(desugar_expr(arena, final_else_branch)); + let desugared_final_else = &*arena.alloc(desugar_expr( + arena, + final_else_branch, + src, + line_info, + module_path, + )); let mut desugared_if_thens = Vec::with_capacity_in(if_thens.len(), arena); for (condition, then_branch) in if_thens.iter() { desugared_if_thens.push(( - *desugar_expr(arena, condition), - *desugar_expr(arena, then_branch), + *desugar_expr(arena, condition, src, line_info, module_path), + *desugar_expr(arena, then_branch, src, line_info, module_path), )); } @@ -453,8 +549,15 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc>) -> &'a Loc }) } Expect(condition, continuation) => { - let desugared_condition = &*arena.alloc(desugar_expr(arena, condition)); - let desugared_continuation = &*arena.alloc(desugar_expr(arena, continuation)); + let desugared_condition = + &*arena.alloc(desugar_expr(arena, condition, src, line_info, module_path)); + let desugared_continuation = &*arena.alloc(desugar_expr( + arena, + continuation, + src, + line_info, + module_path, + )); arena.alloc(Loc { value: Expect(desugared_condition, desugared_continuation), region: loc_expr.region, @@ -463,7 +566,13 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc>) -> &'a Loc Dbg(condition, continuation) => { // Desugars a `dbg x` statement into essentially // Inspect.toInspector x |> Inspect.apply (Inspect.init {}) |> Inspect.toDbgStr |> LowLevelDbg - let desugared_continuation = &*arena.alloc(desugar_expr(arena, continuation)); + let desugared_continuation = &*arena.alloc(desugar_expr( + arena, + continuation, + src, + line_info, + module_path, + )); let region = condition.region; // Inspect.toInspector x @@ -475,7 +584,8 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc>) -> &'a Loc value: to_inspector_fn, region, }); - let desugared_to_inspector_args = arena.alloc([desugar_expr(arena, condition)]); + let desugared_to_inspector_args = + arena.alloc([desugar_expr(arena, condition, src, line_info, module_path)]); let inspector = arena.alloc(Loc { value: Apply( @@ -533,19 +643,41 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc>) -> &'a Loc value: Apply(loc_to_dbg_str_fn_var, to_dbg_str_args, CalledVia::Space), region, }); + + // line_info is an option so that we can lazily calculate it. + // That way it there are no `dbg` statements, we never pay the cast of scanning the source an extra time. + if matches!(line_info, None) { + *line_info = Some(LineInfo::new(src)); + } + let line_col = line_info.as_ref().unwrap().convert_pos(region.start()); + + let dbg_src = src + .split_at(region.start().offset as usize) + .1 + .split_at((region.end().offset - region.start().offset) as usize) + .0; + // |> LowLevelDbg arena.alloc(Loc { - value: LowLevelDbg(dbg_str, desugared_continuation), + value: LowLevelDbg( + arena.alloc(format!("{}:{}", module_path, line_col.line)), + arena.alloc(dbg_src), + dbg_str, + desugared_continuation, + ), region: loc_expr.region, }) } - LowLevelDbg(_, _) => unreachable!("Only exists after desugaring"), + LowLevelDbg(_, _, _, _) => unreachable!("Only exists after desugaring"), } } fn desugar_str_segments<'a>( arena: &'a Bump, segments: &'a [StrSegment<'a>], + src: &'a str, + line_info: &mut Option, + module_path: &str, ) -> &'a [StrSegment<'a>] { Vec::from_iter_in( segments.iter().map(|segment| match segment { @@ -559,6 +691,9 @@ fn desugar_str_segments<'a>( region: loc_expr.region, value: *loc_expr.value, }), + src, + line_info, + module_path, ); StrSegment::Interpolated(Loc { region: loc_desugared.region, @@ -574,6 +709,9 @@ fn desugar_str_segments<'a>( fn desugar_field<'a>( arena: &'a Bump, field: &'a AssignedField<'a, Expr<'a>>, + src: &'a str, + line_info: &mut Option, + module_path: &str, ) -> AssignedField<'a, Expr<'a>> { use roc_parse::ast::AssignedField::*; @@ -584,7 +722,7 @@ fn desugar_field<'a>( region: loc_str.region, }, spaces, - desugar_expr(arena, loc_expr), + desugar_expr(arena, loc_expr, src, line_info, module_path), ), OptionalValue(loc_str, spaces, loc_expr) => OptionalValue( Loc { @@ -592,7 +730,7 @@ fn desugar_field<'a>( region: loc_str.region, }, spaces, - desugar_expr(arena, loc_expr), + desugar_expr(arena, loc_expr, src, line_info, module_path), ), LabelOnly(loc_str) => { // Desugar { x } into { x: x } @@ -610,11 +748,11 @@ fn desugar_field<'a>( region: loc_str.region, }, &[], - desugar_expr(arena, arena.alloc(loc_expr)), + desugar_expr(arena, arena.alloc(loc_expr), src, line_info, module_path), ) } - SpaceBefore(field, _spaces) => desugar_field(arena, field), - SpaceAfter(field, _spaces) => desugar_field(arena, field), + SpaceBefore(field, _spaces) => desugar_field(arena, field, src, line_info, module_path), + SpaceAfter(field, _spaces) => desugar_field(arena, field, src, line_info, module_path), Malformed(string) => Malformed(string), } @@ -623,11 +761,14 @@ fn desugar_field<'a>( fn desugar_loc_patterns<'a>( arena: &'a Bump, loc_patterns: &'a [Loc>], + src: &'a str, + line_info: &mut Option, + module_path: &str, ) -> &'a [Loc>] { Vec::from_iter_in( loc_patterns.iter().map(|loc_pattern| Loc { region: loc_pattern.region, - value: desugar_pattern(arena, loc_pattern.value), + value: desugar_pattern(arena, loc_pattern.value, src, line_info, module_path), }), arena, ) @@ -637,14 +778,23 @@ fn desugar_loc_patterns<'a>( fn desugar_loc_pattern<'a>( arena: &'a Bump, loc_pattern: &'a Loc>, + src: &'a str, + line_info: &mut Option, + module_path: &str, ) -> &'a Loc> { arena.alloc(Loc { region: loc_pattern.region, - value: desugar_pattern(arena, loc_pattern.value), + value: desugar_pattern(arena, loc_pattern.value, src, line_info, module_path), }) } -fn desugar_pattern<'a>(arena: &'a Bump, pattern: Pattern<'a>) -> Pattern<'a> { +fn desugar_pattern<'a>( + arena: &'a Bump, + pattern: Pattern<'a>, + src: &'a str, + line_info: &mut Option, + module_path: &str, +) -> Pattern<'a> { use roc_parse::ast::Pattern::*; match pattern { @@ -667,7 +817,7 @@ fn desugar_pattern<'a>(arena: &'a Bump, pattern: Pattern<'a>) -> Pattern<'a> { let desugared_arg_patterns = Vec::from_iter_in( arg_patterns.iter().map(|arg_pattern| Loc { region: arg_pattern.region, - value: desugar_pattern(arena, arg_pattern.value), + value: desugar_pattern(arena, arg_pattern.value, src, line_info, module_path), }), arena, ) @@ -676,26 +826,62 @@ fn desugar_pattern<'a>(arena: &'a Bump, pattern: Pattern<'a>) -> Pattern<'a> { Apply(tag, desugared_arg_patterns) } RecordDestructure(field_patterns) => { - RecordDestructure(field_patterns.map_items(arena, |field_pattern| Loc { - region: field_pattern.region, - value: desugar_pattern(arena, field_pattern.value), - })) + let mut allocated = Vec::with_capacity_in(field_patterns.len(), arena); + for field_pattern in field_patterns.iter() { + let value = + desugar_pattern(arena, field_pattern.value, src, line_info, module_path); + allocated.push(Loc { + value, + region: field_pattern.region, + }); + } + let field_patterns = field_patterns.replace_items(allocated.into_bump_slice()); + + RecordDestructure(field_patterns) } - RequiredField(name, field_pattern) => { - RequiredField(name, desugar_loc_pattern(arena, field_pattern)) + RequiredField(name, field_pattern) => RequiredField( + name, + desugar_loc_pattern(arena, field_pattern, src, line_info, module_path), + ), + OptionalField(name, expr) => { + OptionalField(name, desugar_expr(arena, expr, src, line_info, module_path)) + } + Tuple(patterns) => { + let mut allocated = Vec::with_capacity_in(patterns.len(), arena); + for pattern in patterns.iter() { + let value = desugar_pattern(arena, pattern.value, src, line_info, module_path); + allocated.push(Loc { + value, + region: pattern.region, + }); + } + let patterns = patterns.replace_items(allocated.into_bump_slice()); + + Tuple(patterns) + } + List(patterns) => { + let mut allocated = Vec::with_capacity_in(patterns.len(), arena); + for pattern in patterns.iter() { + let value = desugar_pattern(arena, pattern.value, src, line_info, module_path); + allocated.push(Loc { + value, + region: pattern.region, + }); + } + let patterns = patterns.replace_items(allocated.into_bump_slice()); + + List(patterns) + } + As(sub_pattern, symbol) => As( + desugar_loc_pattern(arena, sub_pattern, src, line_info, module_path), + symbol, + ), + SpaceBefore(sub_pattern, _spaces) => { + desugar_pattern(arena, *sub_pattern, src, line_info, module_path) + } + SpaceAfter(sub_pattern, _spaces) => { + desugar_pattern(arena, *sub_pattern, src, line_info, module_path) } - OptionalField(name, expr) => OptionalField(name, desugar_expr(arena, expr)), - Tuple(patterns) => Tuple(patterns.map_items(arena, |elem_pattern| Loc { - region: elem_pattern.region, - value: desugar_pattern(arena, elem_pattern.value), - })), - List(patterns) => List(patterns.map_items(arena, |elem_pattern| Loc { - region: elem_pattern.region, - value: desugar_pattern(arena, elem_pattern.value), - })), - As(sub_pattern, symbol) => As(desugar_loc_pattern(arena, sub_pattern), symbol), - SpaceBefore(sub_pattern, _spaces) => desugar_pattern(arena, *sub_pattern), - SpaceAfter(sub_pattern, _spaces) => desugar_pattern(arena, *sub_pattern), } } @@ -824,19 +1010,22 @@ fn desugar_bin_ops<'a>( whole_region: Region, lefts: &'a [(Loc>, Loc)], right: &'a Loc>, + src: &'a str, + line_info: &mut Option, + module_path: &str, ) -> &'a Loc> { let mut arg_stack: Vec<&'a Loc> = Vec::with_capacity_in(lefts.len() + 1, arena); let mut op_stack: Vec> = Vec::with_capacity_in(lefts.len(), arena); for (loc_expr, loc_op) in lefts { - arg_stack.push(desugar_expr(arena, loc_expr)); + arg_stack.push(desugar_expr(arena, loc_expr, src, line_info, module_path)); match run_binop_step(arena, whole_region, &mut arg_stack, &mut op_stack, *loc_op) { Err(problem) => return problem, Ok(()) => continue, } } - let mut expr = desugar_expr(arena, right); + let mut expr = desugar_expr(arena, right, src, line_info, module_path); for (left, loc_op) in arg_stack.into_iter().zip(op_stack.into_iter()).rev() { expr = arena.alloc(new_op_call_expr(arena, left, loc_op, expr)); diff --git a/crates/compiler/can/src/traverse.rs b/crates/compiler/can/src/traverse.rs index eb92dc14da..f92ae773be 100644 --- a/crates/compiler/can/src/traverse.rs +++ b/crates/compiler/can/src/traverse.rs @@ -387,6 +387,8 @@ pub fn walk_expr(visitor: &mut V, expr: &Expr, var: Variable) { } Expr::Dbg { variable, + source: _, + source_location: _, loc_message, loc_continuation, symbol: _, diff --git a/crates/compiler/can/tests/helpers/mod.rs b/crates/compiler/can/tests/helpers/mod.rs index d2f95267c6..9ebe60e662 100644 --- a/crates/compiler/can/tests/helpers/mod.rs +++ b/crates/compiler/can/tests/helpers/mod.rs @@ -52,7 +52,13 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut // visited a BinOp node we'd recursively try to apply this to each of its nested // operators, and then again on *their* nested operators, ultimately applying the // rules multiple times unnecessarily. - let loc_expr = operator::desugar_expr(arena, &loc_expr); + let loc_expr = operator::desugar_expr( + arena, + &loc_expr, + expr_str, + &mut None, + arena.alloc("TestPath"), + ); let mut scope = Scope::new(home, IdentIds::default(), Default::default()); scope.add_alias( diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index 0779f8cfcd..6c64843c72 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -741,6 +741,8 @@ pub fn constrain_expr( } Dbg { + source_location: _, + source: _, loc_message, loc_continuation, variable, diff --git a/crates/compiler/fmt/src/expr.rs b/crates/compiler/fmt/src/expr.rs index 39e6bce4ee..0f83170331 100644 --- a/crates/compiler/fmt/src/expr.rs +++ b/crates/compiler/fmt/src/expr.rs @@ -62,7 +62,7 @@ impl<'a> Formattable for Expr<'a> { condition.is_multiline() || continuation.is_multiline() } Dbg(condition, continuation) => condition.is_multiline() || continuation.is_multiline(), - LowLevelDbg(_, _) => unreachable!( + LowLevelDbg(_, _, _, _) => unreachable!( "LowLevelDbg should only exist after desugaring, not during formatting" ), @@ -438,7 +438,7 @@ impl<'a> Formattable for Expr<'a> { Dbg(condition, continuation) => { fmt_dbg(buf, condition, continuation, self.is_multiline(), indent); } - LowLevelDbg(_, _) => unreachable!( + LowLevelDbg(_, _, _, _) => unreachable!( "LowLevelDbg should only exist after desugaring, not during formatting" ), If(branches, final_else) => { diff --git a/crates/compiler/fmt/src/spaces.rs b/crates/compiler/fmt/src/spaces.rs index 255b18ae56..9568f65e53 100644 --- a/crates/compiler/fmt/src/spaces.rs +++ b/crates/compiler/fmt/src/spaces.rs @@ -726,7 +726,7 @@ impl<'a> RemoveSpaces<'a> for Expr<'a> { arena.alloc(a.remove_spaces(arena)), arena.alloc(b.remove_spaces(arena)), ), - Expr::LowLevelDbg(_, _) => unreachable!( + Expr::LowLevelDbg(_, _, _, _) => unreachable!( "LowLevelDbg should only exist after desugaring, not during formatting" ), Expr::Apply(a, b, c) => Expr::Apply( diff --git a/crates/compiler/gen_llvm/src/llvm/build.rs b/crates/compiler/gen_llvm/src/llvm/build.rs index 8500e42607..6231d42cc2 100644 --- a/crates/compiler/gen_llvm/src/llvm/build.rs +++ b/crates/compiler/gen_llvm/src/llvm/build.rs @@ -910,16 +910,18 @@ impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> { &self, env: &Env<'a, 'ctx, 'env>, location: BasicValueEnum<'ctx>, + source: BasicValueEnum<'ctx>, message: BasicValueEnum<'ctx>, ) { let function = self.module.get_function("roc_dbg").unwrap(); let loc = self.string_to_arg(env, location); + let src = self.string_to_arg(env, source); let msg = self.string_to_arg(env, message); - let call = self - .builder - .new_build_call(function, &[loc.into(), msg.into()], "roc_dbg"); + let call = + self.builder + .new_build_call(function, &[loc.into(), src.into(), msg.into()], "roc_dbg"); call.set_call_convention(C_CALL_CONV); } @@ -3527,17 +3529,17 @@ pub(crate) fn build_exp_stmt<'a, 'ctx>( } Dbg { + source_location, + source, symbol, variable: _, remainder, } => { if env.mode.runs_expects() { - // TODO: Change location to `filename:line_number` - // let region = unsafe { std::mem::transmute::<_, roc_region::all::Region>(*symbol) }; - let location = - build_string_literal(env, parent, symbol.module_string(&env.interns)); + let location = build_string_literal(env, parent, source_location); + let source = build_string_literal(env, parent, source); let message = scope.load_symbol(symbol); - env.call_dbg(env, location, message); + env.call_dbg(env, location, source, message); } build_exp_stmt( diff --git a/crates/compiler/load/tests/helpers/mod.rs b/crates/compiler/load/tests/helpers/mod.rs index cb782897d1..ee8283ceaf 100644 --- a/crates/compiler/load/tests/helpers/mod.rs +++ b/crates/compiler/load/tests/helpers/mod.rs @@ -166,7 +166,13 @@ pub fn can_expr_with<'a>( // visited a BinOp node we'd recursively try to apply this to each of its nested // operators, and then again on *their* nested operators, ultimately applying the // rules multiple times unnecessarily. - let loc_expr = operator::desugar_expr(arena, &loc_expr); + let loc_expr = operator::desugar_expr( + arena, + &loc_expr, + expr_str, + &mut None, + arena.alloc("TestPath"), + ); let mut scope = Scope::new(home, IdentIds::default(), Default::default()); diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index d24f8b96e2..b3190019db 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -5264,6 +5264,8 @@ fn canonicalize_and_constrain<'a>( let ParsedModule { module_id, + module_path, + src, header_type, exposed_ident_ids, parsed_defs, @@ -5286,6 +5288,8 @@ fn canonicalize_and_constrain<'a>( parsed_defs, &header_type, module_id, + &*arena.alloc(module_path.to_string_lossy()), + src, module_ids, exposed_ident_ids, &dep_idents, diff --git a/crates/compiler/mono/src/drop_specialization.rs b/crates/compiler/mono/src/drop_specialization.rs index 1317aa53bf..627db6c9d0 100644 --- a/crates/compiler/mono/src/drop_specialization.rs +++ b/crates/compiler/mono/src/drop_specialization.rs @@ -631,10 +631,14 @@ fn specialize_drops_stmt<'a, 'i>( ), }), Stmt::Dbg { + source_location, + source, symbol, variable, remainder, } => arena.alloc(Stmt::Dbg { + source_location, + source, symbol: *symbol, variable: *variable, remainder: specialize_drops_stmt( diff --git a/crates/compiler/mono/src/inc_dec.rs b/crates/compiler/mono/src/inc_dec.rs index 96c529bbb1..48593e87c4 100644 --- a/crates/compiler/mono/src/inc_dec.rs +++ b/crates/compiler/mono/src/inc_dec.rs @@ -689,6 +689,8 @@ fn insert_refcount_operations_stmt<'v, 'a>( }) } Stmt::Dbg { + source_location, + source, symbol, variable, remainder, @@ -703,6 +705,8 @@ fn insert_refcount_operations_stmt<'v, 'a>( ); arena.alloc(Stmt::Dbg { + source_location, + source, symbol: *symbol, variable: *variable, remainder: newer_remainder, diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index 07fd3880fa..2b31d66339 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -1531,6 +1531,10 @@ pub enum Stmt<'a> { remainder: &'a Stmt<'a>, }, Dbg { + /// The location this dbg is in source as a printable string. + source_location: &'a str, + /// The source code of the expression being debugged. + source: &'a str, /// The expression we're displaying symbol: Symbol, /// The specialized variable of the expression @@ -4602,6 +4606,8 @@ pub fn with_hole<'a>( Expect { .. } => unreachable!("I think this is unreachable"), ExpectFx { .. } => unreachable!("I think this is unreachable"), Dbg { + source_location, + source, loc_message, loc_continuation, variable: cond_variable, @@ -4621,6 +4627,8 @@ pub fn with_hole<'a>( env, procs, layout_cache, + &*arena.alloc(source_location), + &*arena.alloc(source), dbg_symbol, *loc_message, cond_variable, @@ -5892,8 +5900,10 @@ fn compile_dbg<'a>( env: &mut Env<'a, '_>, procs: &mut Procs<'a>, layout_cache: &mut LayoutCache<'a>, + source_location: &'a str, + source: &'a str, dbg_symbol: Symbol, - loc_condition: Loc, + loc_message: Loc, variable: Variable, continuation: Stmt<'a>, ) -> Stmt<'a> { @@ -5904,6 +5914,8 @@ fn compile_dbg<'a>( .fresh_unnamed_flex_var(); let dbg_stmt = Stmt::Dbg { + source_location, + source, symbol: dbg_symbol, variable: spec_var, remainder: env.arena.alloc(continuation), @@ -5914,17 +5926,17 @@ fn compile_dbg<'a>( store_specialized_expectation_lookups(env, [variable], &[spec_var]); let symbol_is_reused = matches!( - can_reuse_symbol(env, layout_cache, procs, &loc_condition.value, variable), + can_reuse_symbol(env, layout_cache, procs, &loc_message.value, variable), ReuseSymbol::Value(_) ); - // skip evaluating the condition if it's just a symbol + // skip evaluating the message if it's just a symbol if symbol_is_reused { dbg_stmt } else { with_hole( env, - loc_condition.value, + loc_message.value, variable, procs, layout_cache, @@ -7137,6 +7149,8 @@ pub fn from_can<'a>( } Dbg { + source_location, + source, loc_message, loc_continuation, variable: cond_variable, @@ -7148,6 +7162,8 @@ pub fn from_can<'a>( env, procs, layout_cache, + &*env.arena.alloc(source_location), + &*env.arena.alloc(source), dbg_symbol, *loc_message, cond_variable, @@ -7621,6 +7637,8 @@ fn substitute_in_stmt_help<'a>( } Dbg { + source_location, + source, symbol, variable, remainder, @@ -7629,6 +7647,8 @@ fn substitute_in_stmt_help<'a>( substitute_in_stmt_help(arena, remainder, subs).unwrap_or(remainder); let expect = Dbg { + source_location, + source, symbol: substitute(subs, *symbol).unwrap_or(*symbol), variable: *variable, remainder: new_remainder, diff --git a/crates/compiler/mono/src/reset_reuse.rs b/crates/compiler/mono/src/reset_reuse.rs index cb893903dc..f6a8bd7c52 100644 --- a/crates/compiler/mono/src/reset_reuse.rs +++ b/crates/compiler/mono/src/reset_reuse.rs @@ -651,6 +651,8 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>( }) } Stmt::Dbg { + source_location, + source, symbol, variable, remainder, @@ -666,6 +668,8 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>( ); arena.alloc(Stmt::Dbg { + source_location, + source, symbol: *symbol, variable: *variable, remainder: new_remainder, diff --git a/crates/compiler/mono/src/tail_recursion.rs b/crates/compiler/mono/src/tail_recursion.rs index 5b6bcd29f4..ad827e366e 100644 --- a/crates/compiler/mono/src/tail_recursion.rs +++ b/crates/compiler/mono/src/tail_recursion.rs @@ -330,6 +330,8 @@ fn insert_jumps<'a>( } Dbg { + source_location, + source, symbol, variable, remainder, @@ -342,6 +344,8 @@ fn insert_jumps<'a>( needle_result, ) { Some(cont) => Some(arena.alloc(Dbg { + source_location, + source, symbol: *symbol, variable: *variable, remainder: cont, @@ -1020,10 +1024,14 @@ impl<'a> TrmcEnv<'a> { remainder: arena.alloc(self.walk_stmt(env, remainder)), }, Stmt::Dbg { + source_location, + source, symbol, variable, remainder, } => Stmt::Dbg { + source_location, + source, symbol: *symbol, variable: *variable, remainder: arena.alloc(self.walk_stmt(env, remainder)), diff --git a/crates/compiler/parse/src/ast.rs b/crates/compiler/parse/src/ast.rs index 949d88b443..1138585bca 100644 --- a/crates/compiler/parse/src/ast.rs +++ b/crates/compiler/parse/src/ast.rs @@ -302,7 +302,7 @@ pub enum Expr<'a> { Expect(&'a Loc>, &'a Loc>), Dbg(&'a Loc>, &'a Loc>), // This form of debug is a desugared call to roc_dbg - LowLevelDbg(&'a Loc>, &'a Loc>), + LowLevelDbg(&'a str, &'a str, &'a Loc>, &'a Loc>), // Application /// To apply by name, do Apply(Var(...), ...) @@ -1537,7 +1537,7 @@ impl<'a> Malformed for Expr<'a> { Backpassing(args, call, body) => args.iter().any(|arg| arg.is_malformed()) || call.is_malformed() || body.is_malformed(), Expect(condition, continuation) | Dbg(condition, continuation) => condition.is_malformed() || continuation.is_malformed(), - LowLevelDbg(condition, continuation) => condition.is_malformed() || continuation.is_malformed(), + LowLevelDbg(_, _, condition, continuation) => condition.is_malformed() || continuation.is_malformed(), Apply(func, args, _) => func.is_malformed() || args.iter().any(|arg| arg.is_malformed()), BinOps(firsts, last) => firsts.iter().any(|(expr, _)| expr.is_malformed()) || last.is_malformed(), UnaryOp(expr, _) => expr.is_malformed(), diff --git a/crates/compiler/parse/src/expr.rs b/crates/compiler/parse/src/expr.rs index f51602a48d..da2ad19847 100644 --- a/crates/compiler/parse/src/expr.rs +++ b/crates/compiler/parse/src/expr.rs @@ -1933,7 +1933,7 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result> { Expr::Dbg(e1, e2) => (e1.iter_tokens(arena).into_iter()) .chain(e2.iter_tokens(arena)) .collect_in(arena), - Expr::LowLevelDbg(e1, e2) => (e1.iter_tokens(arena).into_iter()) + Expr::LowLevelDbg(_, _, e1, e2) => (e1.iter_tokens(arena).into_iter()) .chain(e2.iter_tokens(arena)) .collect_in(arena), Expr::Apply(e1, e2, _called_via) => (e1.iter_tokens(arena).into_iter())